Repository: emcie-co/parlant Branch: develop Commit: d1299ad3b7d5 Files: 478 Total size: 7.1 MB Directory structure: gitextract_lshtm9pg/ ├── .devcontainer/ │ ├── Dockerfile │ └── devcontainer.json ├── .githooks/ │ ├── pre-commit │ ├── pre-push │ └── prepare-commit-msg ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.md │ │ └── feature-request.md │ ├── dco.yml │ └── workflows/ │ ├── ci-test.yml │ ├── docker-publish.yml │ └── lint.yml ├── .gitignore ├── CHANGELOG.md ├── CLAUDE.md ├── CONTRIBUTING.md ├── DCO.md ├── LICENSE ├── README.md ├── docs/ │ ├── adapters/ │ │ ├── nlp/ │ │ │ ├── azure.md │ │ │ ├── ollama.md │ │ │ ├── openrouter.md │ │ │ ├── snowflake-cortex.md │ │ │ └── vertex.md │ │ ├── persistence/ │ │ │ └── snowflake.md │ │ └── vector_db/ │ │ └── qdrant.md │ ├── advanced/ │ │ ├── contributing.md │ │ ├── custom-llms.md │ │ ├── engine-extensions.md │ │ └── explainability.md │ ├── concepts/ │ │ ├── customization/ │ │ │ ├── canned-responses.md │ │ │ ├── glossary.md │ │ │ ├── guidelines.md │ │ │ ├── journeys.md │ │ │ ├── relationships.md │ │ │ ├── retrievers.md │ │ │ ├── tools.md │ │ │ └── variables.md │ │ ├── entities/ │ │ │ ├── agents.md │ │ │ └── customers.md │ │ └── sessions.md │ ├── interactions.md │ ├── production/ │ │ ├── agentic-design.md │ │ ├── api-hardening.md │ │ ├── custom-frontend.md │ │ ├── human-handoff.md │ │ └── input-moderation.md │ └── quickstart/ │ ├── examples.md │ ├── installation.md │ └── motivation.md ├── examples/ │ ├── healthcare.py │ └── travel_voice_agent.py ├── llms.txt ├── mypy.ini ├── pyproject.toml ├── pytest.ini ├── pytest_stochastics.json ├── ruff.toml ├── scripts/ │ ├── ci/ │ │ └── github_action_ubuntu_2404_free_space.sh │ ├── fern/ │ │ ├── docs.yml │ │ ├── fern.config.json │ │ └── generators.yml │ ├── generate_client_sdk.py │ ├── initialize_repo.py │ ├── install_packages.py │ ├── lint.py │ ├── publish.py │ ├── utils.py │ └── version.py ├── src/ │ └── parlant/ │ ├── adapters/ │ │ ├── db/ │ │ │ ├── json_file.py │ │ │ ├── mongo_db.py │ │ │ ├── snowflake_db.py │ │ │ └── transient.py │ │ ├── loggers/ │ │ │ ├── opentelemetry.py │ │ │ └── websocket.py │ │ ├── meter/ │ │ │ └── opentelemetry.py │ │ ├── nlp/ │ │ │ ├── anthropic_service.py │ │ │ ├── aws_service.py │ │ │ ├── azure_service.py │ │ │ ├── cerebras_service.py │ │ │ ├── common.py │ │ │ ├── deepseek_service.py │ │ │ ├── emcie_service.py │ │ │ ├── fireworks_service.py │ │ │ ├── gemini_service.py │ │ │ ├── glm_service.py │ │ │ ├── hugging_face.py │ │ │ ├── lakera.py │ │ │ ├── litellm_service.py │ │ │ ├── mistral_service.py │ │ │ ├── modelscope_service.py │ │ │ ├── ollama_service.py │ │ │ ├── openai_service.py │ │ │ ├── openrouter_service.py │ │ │ ├── qwen_service.py │ │ │ ├── snowflake_cortex_service.py │ │ │ ├── together_service.py │ │ │ ├── vertex_service.py │ │ │ └── zhipu_service.py │ │ ├── tracing/ │ │ │ └── opentelemetry.py │ │ └── vector_db/ │ │ ├── chroma.py │ │ ├── qdrant.py │ │ └── transient.py │ ├── api/ │ │ ├── agents.py │ │ ├── app.py │ │ ├── authorization.py │ │ ├── canned_responses.py │ │ ├── capabilities.py │ │ ├── chat/ │ │ │ ├── .gitignore │ │ │ ├── .prettierrc │ │ │ ├── .vite/ │ │ │ │ └── deps_temp_0491001f/ │ │ │ │ └── package.json │ │ │ ├── components.json │ │ │ ├── dist/ │ │ │ │ ├── assets/ │ │ │ │ │ ├── index-BBAJ1vle.js │ │ │ │ │ ├── index-BRVifGSy.css │ │ │ │ │ └── manifest-BRNJYplA.webmanifest │ │ │ │ ├── fonts/ │ │ │ │ │ ├── Inter/ │ │ │ │ │ │ └── inter.css │ │ │ │ │ ├── ibm-plex-mono/ │ │ │ │ │ │ ├── ibm-plex-mono.css │ │ │ │ │ │ └── static/ │ │ │ │ │ │ └── OFL.txt │ │ │ │ │ ├── ubuntu-mono/ │ │ │ │ │ │ ├── static/ │ │ │ │ │ │ │ └── UFL.txt │ │ │ │ │ │ └── ubuntu_mono.css │ │ │ │ │ └── ubuntu-sans/ │ │ │ │ │ ├── README.txt │ │ │ │ │ ├── UFL.txt │ │ │ │ │ └── ubuntu_sans.css │ │ │ │ └── index.html │ │ │ ├── eslint.config.js │ │ │ ├── index.html │ │ │ ├── manifest.webmanifest │ │ │ ├── package.json │ │ │ ├── postcss.config.js │ │ │ ├── public/ │ │ │ │ └── fonts/ │ │ │ │ ├── Inter/ │ │ │ │ │ └── inter.css │ │ │ │ ├── ibm-plex-mono/ │ │ │ │ │ ├── ibm-plex-mono.css │ │ │ │ │ └── static/ │ │ │ │ │ └── OFL.txt │ │ │ │ ├── ubuntu-mono/ │ │ │ │ │ ├── static/ │ │ │ │ │ │ └── UFL.txt │ │ │ │ │ └── ubuntu_mono.css │ │ │ │ └── ubuntu-sans/ │ │ │ │ ├── README.txt │ │ │ │ ├── UFL.txt │ │ │ │ └── ubuntu_sans.css │ │ │ ├── setupTests.ts │ │ │ ├── src/ │ │ │ │ ├── App.css │ │ │ │ ├── App.tsx │ │ │ │ ├── components/ │ │ │ │ │ ├── agents-list/ │ │ │ │ │ │ ├── agent-list.module.scss │ │ │ │ │ │ └── agent-list.tsx │ │ │ │ │ ├── avatar/ │ │ │ │ │ │ └── avatar.tsx │ │ │ │ │ ├── canned-response/ │ │ │ │ │ │ └── canned-response.tsx │ │ │ │ │ ├── canned-responses/ │ │ │ │ │ │ └── canned-responses.tsx │ │ │ │ │ ├── chat-header/ │ │ │ │ │ │ └── chat-header.tsx │ │ │ │ │ ├── chatbot/ │ │ │ │ │ │ └── chatbot.tsx │ │ │ │ │ ├── dark-mode-toggle/ │ │ │ │ │ │ └── dark-mode-toggle.tsx │ │ │ │ │ ├── error-boundary/ │ │ │ │ │ │ └── error-boundary.tsx │ │ │ │ │ ├── gradient-button/ │ │ │ │ │ │ ├── gradient-button.module.scss │ │ │ │ │ │ └── gradient-button.tsx │ │ │ │ │ ├── header-wrapper/ │ │ │ │ │ │ └── header-wrapper.tsx │ │ │ │ │ ├── log-filters/ │ │ │ │ │ │ └── log-filters.tsx │ │ │ │ │ ├── markdown/ │ │ │ │ │ │ └── markdown.tsx │ │ │ │ │ ├── message/ │ │ │ │ │ │ ├── draft-bubble.tsx │ │ │ │ │ │ ├── message-bubble.tsx │ │ │ │ │ │ ├── message-relative-time.tsx │ │ │ │ │ │ ├── message.module.scss │ │ │ │ │ │ └── message.tsx │ │ │ │ │ ├── message-details/ │ │ │ │ │ │ ├── empty-state.tsx │ │ │ │ │ │ ├── filter-tabs.tsx │ │ │ │ │ │ ├── flag-message.tsx │ │ │ │ │ │ ├── indexeddb-data.tsx │ │ │ │ │ │ ├── message-details-header.tsx │ │ │ │ │ │ ├── message-details.tsx │ │ │ │ │ │ ├── message-log.tsx │ │ │ │ │ │ └── message-logs.tsx │ │ │ │ │ ├── progress-logo/ │ │ │ │ │ │ └── progress-logo.tsx │ │ │ │ │ ├── session-list/ │ │ │ │ │ │ ├── session-list-item/ │ │ │ │ │ │ │ ├── session-list-item.module.scss │ │ │ │ │ │ │ └── session-list-item.tsx │ │ │ │ │ │ └── session-list.tsx │ │ │ │ │ ├── session-view/ │ │ │ │ │ │ ├── date-header/ │ │ │ │ │ │ │ └── date-header.tsx │ │ │ │ │ │ ├── session-view-header/ │ │ │ │ │ │ │ └── session-view-header.tsx │ │ │ │ │ │ ├── session-view.module.scss │ │ │ │ │ │ └── session-view.tsx │ │ │ │ │ ├── ui/ │ │ │ │ │ │ ├── button.tsx │ │ │ │ │ │ ├── checkbox.tsx │ │ │ │ │ │ ├── custom/ │ │ │ │ │ │ │ ├── copy-text.tsx │ │ │ │ │ │ │ ├── line-no-div.tsx │ │ │ │ │ │ │ ├── spacer.tsx │ │ │ │ │ │ │ └── tooltip.tsx │ │ │ │ │ │ ├── dialog.tsx │ │ │ │ │ │ ├── drawer.tsx │ │ │ │ │ │ ├── dropdown-menu.tsx │ │ │ │ │ │ ├── input.tsx │ │ │ │ │ │ ├── radio-group.tsx │ │ │ │ │ │ ├── resizable.tsx │ │ │ │ │ │ ├── select.tsx │ │ │ │ │ │ ├── sheet.tsx │ │ │ │ │ │ ├── skeleton.tsx │ │ │ │ │ │ ├── sonner.tsx │ │ │ │ │ │ ├── switch.tsx │ │ │ │ │ │ ├── textarea.tsx │ │ │ │ │ │ └── tooltip.tsx │ │ │ │ │ └── virtual-scroll/ │ │ │ │ │ └── virtual-scroll.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── useDialog.tsx │ │ │ │ │ ├── useFetch.tsx │ │ │ │ │ ├── useLocalStorage.ts │ │ │ │ │ ├── useQuestionDialog.tsx │ │ │ │ │ └── useWebSocket.ts │ │ │ │ ├── index.css │ │ │ │ ├── lib/ │ │ │ │ │ ├── broadcast-channel.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── main.tsx │ │ │ │ ├── store.ts │ │ │ │ ├── utils/ │ │ │ │ │ ├── api.ts │ │ │ │ │ ├── date.tsx │ │ │ │ │ ├── interfaces.tsx │ │ │ │ │ ├── logs.ts │ │ │ │ │ ├── methods.tsx │ │ │ │ │ ├── obj.tsx │ │ │ │ │ └── sounds.ts │ │ │ │ └── vite-env.d.ts │ │ │ ├── tailwind.config.js │ │ │ ├── tsconfig.app.json │ │ │ ├── tsconfig.app.tsbuildinfo │ │ │ ├── tsconfig.json │ │ │ ├── tsconfig.node.json │ │ │ ├── tsconfig.node.tsbuildinfo │ │ │ └── vite.config.ts │ │ ├── common.py │ │ ├── context_variables.py │ │ ├── customers.py │ │ ├── evaluations.py │ │ ├── glossary.py │ │ ├── guidelines.py │ │ ├── journeys.py │ │ ├── logs.py │ │ ├── relationships.py │ │ ├── services.py │ │ ├── sessions.py │ │ └── tags.py │ ├── bin/ │ │ ├── client.py │ │ ├── prepare_migration.py │ │ └── server.py │ ├── core/ │ │ ├── agents.py │ │ ├── app_modules/ │ │ │ ├── agents.py │ │ │ ├── canned_responses.py │ │ │ ├── capabilities.py │ │ │ ├── common.py │ │ │ ├── context_variables.py │ │ │ ├── customers.py │ │ │ ├── evaluations.py │ │ │ ├── glossary.py │ │ │ ├── guidelines.py │ │ │ ├── journeys.py │ │ │ ├── relationships.py │ │ │ ├── services.py │ │ │ ├── sessions.py │ │ │ └── tags.py │ │ ├── application.py │ │ ├── async_utils.py │ │ ├── background_tasks.py │ │ ├── canned_responses.py │ │ ├── capabilities.py │ │ ├── common.py │ │ ├── context_variables.py │ │ ├── customers.py │ │ ├── emission/ │ │ │ ├── event_buffer.py │ │ │ └── event_publisher.py │ │ ├── emissions.py │ │ ├── engines/ │ │ │ ├── alpha/ │ │ │ │ ├── canned_response_generator.py │ │ │ │ ├── engine.py │ │ │ │ ├── engine_context.py │ │ │ │ ├── entity_context.py │ │ │ │ ├── guideline_matching/ │ │ │ │ │ ├── common.py │ │ │ │ │ ├── custom_guideline_matching_strategy.py │ │ │ │ │ ├── generic/ │ │ │ │ │ │ ├── common.py │ │ │ │ │ │ ├── disambiguation_batch.py │ │ │ │ │ │ ├── generic_guideline_matching_strategy.py │ │ │ │ │ │ ├── guideline_actionable_batch.py │ │ │ │ │ │ ├── guideline_low_criticality_batch.py │ │ │ │ │ │ ├── guideline_previously_applied_actionable_batch.py │ │ │ │ │ │ ├── guideline_previously_applied_actionable_customer_dependent_batch.py │ │ │ │ │ │ ├── journey/ │ │ │ │ │ │ │ ├── journey_backtrack_check.py │ │ │ │ │ │ │ ├── journey_backtrack_node_selection.py │ │ │ │ │ │ │ ├── journey_next_step_selection.py │ │ │ │ │ │ │ └── journey_node_selection_batch.py │ │ │ │ │ │ ├── observational_batch.py │ │ │ │ │ │ └── response_analysis_batch.py │ │ │ │ │ ├── generic_guideline_matching_strategy_resolver.py │ │ │ │ │ ├── guideline_match.py │ │ │ │ │ ├── guideline_matcher.py │ │ │ │ │ └── guideline_matching_context.py │ │ │ │ ├── hooks.py │ │ │ │ ├── message_event_composer.py │ │ │ │ ├── message_generator.py │ │ │ │ ├── optimization_policy.py │ │ │ │ ├── perceived_performance_policy.py │ │ │ │ ├── planners.py │ │ │ │ ├── planning/ │ │ │ │ │ └── __init__.py │ │ │ │ ├── prompt_builder.py │ │ │ │ ├── relational_resolver.py │ │ │ │ ├── tool_calling/ │ │ │ │ │ ├── default_tool_call_batcher.py │ │ │ │ │ ├── overlapping_tools_batch.py │ │ │ │ │ ├── single_tool_batch.py │ │ │ │ │ └── tool_caller.py │ │ │ │ ├── tool_event_generator.py │ │ │ │ └── utils.py │ │ │ └── types.py │ │ ├── entity_cq.py │ │ ├── evaluations.py │ │ ├── glossary.py │ │ ├── guideline_tool_associations.py │ │ ├── guidelines.py │ │ ├── journey_guideline_projection.py │ │ ├── journeys.py │ │ ├── loggers.py │ │ ├── meter.py │ │ ├── nlp/ │ │ │ ├── embedding.py │ │ │ ├── generation.py │ │ │ ├── generation_info.py │ │ │ ├── moderation.py │ │ │ ├── policies.py │ │ │ ├── service.py │ │ │ └── tokenization.py │ │ ├── persistence/ │ │ │ ├── common.py │ │ │ ├── data_collection.py │ │ │ ├── document_database.py │ │ │ ├── document_database_helper.py │ │ │ ├── vector_database.py │ │ │ └── vector_database_helper.py │ │ ├── relationships.py │ │ ├── services/ │ │ │ ├── indexing/ │ │ │ │ ├── behavioral_change_evaluation.py │ │ │ │ ├── common.py │ │ │ │ ├── customer_dependent_action_detector.py │ │ │ │ ├── guideline_action_proposer.py │ │ │ │ ├── guideline_agent_intention_proposer.py │ │ │ │ ├── guideline_continuous_proposer.py │ │ │ │ ├── journey_reachable_nodes_evaluation.py │ │ │ │ ├── relative_action_proposer.py │ │ │ │ └── tool_running_action_detector.py │ │ │ └── tools/ │ │ │ ├── mcp_service.py │ │ │ ├── openapi.py │ │ │ ├── plugins.py │ │ │ └── service_registry.py │ │ ├── sessions.py │ │ ├── shots.py │ │ ├── tags.py │ │ ├── tools.py │ │ ├── tracer.py │ │ └── version.py │ ├── py.typed │ └── sdk.py └── tests/ ├── __init__.py ├── adapters/ │ ├── db/ │ │ ├── test_chroma.py │ │ ├── test_json_file.py │ │ ├── test_mongodb.py │ │ └── test_snowflake_db.py │ ├── nlp/ │ │ ├── test_azure_service.py │ │ ├── test_litellm_service.py │ │ ├── test_openrouter_service.py │ │ ├── test_qwen_service.py │ │ └── test_zhipu_service.py │ └── vector_db/ │ ├── test_qdrant.py │ └── test_transient.py ├── api/ │ ├── conftest.py │ ├── test_agents.py │ ├── test_app.py │ ├── test_authorization.py │ ├── test_canned_responses.py │ ├── test_capabilities.py │ ├── test_context_variables.py │ ├── test_customers.py │ ├── test_evaluations.py │ ├── test_glossary.py │ ├── test_guidelines.py │ ├── test_journeys.py │ ├── test_relationships.py │ ├── test_services.py │ ├── test_sessions.py │ ├── test_tags.py │ └── test_websocket_logger.py ├── conftest.py ├── core/ │ ├── .gitkeep │ ├── common/ │ │ ├── engines/ │ │ │ └── alpha/ │ │ │ ├── steps/ │ │ │ │ ├── agents.py │ │ │ │ ├── canned_responses.py │ │ │ │ ├── capabilities.py │ │ │ │ ├── context_variables.py │ │ │ │ ├── customers.py │ │ │ │ ├── engines.py │ │ │ │ ├── events.py │ │ │ │ ├── guidelines.py │ │ │ │ ├── journeys.py │ │ │ │ ├── sessions.py │ │ │ │ ├── tags.py │ │ │ │ ├── terms.py │ │ │ │ └── tools.py │ │ │ └── utils.py │ │ └── utils.py │ ├── conftest.py │ ├── stable/ │ │ ├── engines/ │ │ │ └── alpha/ │ │ │ ├── features/ │ │ │ │ ├── baseline/ │ │ │ │ │ ├── capabilities.feature │ │ │ │ │ ├── context_variables.feature │ │ │ │ │ ├── conversation.feature │ │ │ │ │ ├── errors.feature │ │ │ │ │ ├── glossary.feature │ │ │ │ │ ├── journeys.feature │ │ │ │ │ ├── moderation.feature │ │ │ │ │ ├── proactivity.feature │ │ │ │ │ ├── relationships.feature │ │ │ │ │ ├── strict_canned_responses.feature │ │ │ │ │ ├── strict_canned_responses_capabilities.feature │ │ │ │ │ ├── supervision.feature │ │ │ │ │ ├── tools.feature │ │ │ │ │ └── triggered_utterances.feature │ │ │ │ └── user_stories/ │ │ │ │ └── conversation.feature │ │ │ ├── test_baseline_scenarios.py │ │ │ ├── test_context_variable_loading.py │ │ │ ├── test_disambiguation_batch.py │ │ │ ├── test_generic_response_analysis.py │ │ │ ├── test_guideline_actionable_batch.py │ │ │ ├── test_guideline_low_criticality_batch.py │ │ │ ├── test_guideline_matcher.py │ │ │ ├── test_journey_node_selection.py │ │ │ ├── test_mcp.py │ │ │ ├── test_previously_applied_actionable_batch.py │ │ │ ├── test_previously_applied_actionable_customer_dependent_batch.py │ │ │ ├── test_relational_resolver.py │ │ │ ├── test_tool_caller.py │ │ │ └── test_user_story_scenarios.py │ │ ├── nlp/ │ │ │ ├── test_embedding.py │ │ │ └── test_generation.py │ │ ├── persistence/ │ │ │ └── test_matches_filters.py │ │ ├── services/ │ │ │ ├── indexing/ │ │ │ │ ├── test_agent_intention_proposer.py │ │ │ │ ├── test_continuous_guideline_proposer.py │ │ │ │ ├── test_customer_dependent_action_detector.py │ │ │ │ ├── test_guideline_action_proposer.py │ │ │ │ ├── test_relative_action_step_proposer.py │ │ │ │ └── test_tool_running_action_detector.py │ │ │ └── tools/ │ │ │ ├── test_openapi.py │ │ │ └── test_plugin_client.py │ │ ├── test_application.py │ │ ├── test_capability_vector_store.py │ │ ├── test_entity_cq.py │ │ ├── test_journey_guideline_projection.py │ │ └── test_relationships.py │ ├── test_cancellation_suppression_latch.py │ ├── test_id_generator.py │ └── unstable/ │ └── engines/ │ └── alpha/ │ ├── features/ │ │ ├── baseline/ │ │ │ ├── conversation.feature │ │ │ ├── fluid_canned_responses.feature │ │ │ ├── glossary.feature │ │ │ ├── strict_canned_responses.feature │ │ │ ├── supervision.feature │ │ │ └── tools.feature │ │ └── user_stories/ │ │ └── conversation.feature │ ├── test_agent_intention_proposer.py │ ├── test_baseline_scenarios.py │ ├── test_disambiguation_batch.py │ ├── test_guideline_matcher.py │ ├── test_journey_node_selection.py │ ├── test_previously_applied_actionable_batch.py │ └── test_user_story_scenarios.py ├── data/ │ └── get_products_by_type_data.json ├── e2e/ │ ├── conftest.py │ ├── test_client_cli_via_api.py │ ├── test_server_cli.py │ └── test_utilities.py ├── modules/ │ ├── bank.py │ ├── mcp_parrot.py │ └── tech_store.py ├── sdk/ │ ├── conftest.py │ ├── test_agents.py │ ├── test_canned_responses.py │ ├── test_current_entities.py │ ├── test_customers.py │ ├── test_dynamic_composition_mode.py │ ├── test_glossary.py │ ├── test_guidelines.py │ ├── test_journeys.py │ ├── test_labels.py │ ├── test_planners.py │ ├── test_retrievers.py │ ├── test_sdk_validation.py │ ├── test_server.py │ ├── test_tools.py │ ├── test_variables.py │ └── utils.py ├── test_utilities.py └── tool_utilities.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .devcontainer/Dockerfile ================================================ FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 ARG DEBIAN_FRONTEND=noninteractive ARG USER=vscode RUN DEBIAN_FRONTEND=noninteractive \ && apt-get update \ && apt-get install -y build-essential --no-install-recommends make \ ca-certificates \ bash-completion \ git \ just \ libssl-dev \ zlib1g-dev \ libbz2-dev \ libreadline-dev \ libsqlite3-dev \ wget \ curl \ llvm \ libncurses5-dev \ xz-utils \ tk-dev \ libxml2-dev \ libxmlsec1-dev \ libffi-dev \ liblzma-dev # Python and poetry installation USER $USER ARG HOME="/home/$USER" ARG PYTHON_VERSION=3.10 ENV PYENV_ROOT="${HOME}/.pyenv" ENV PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${HOME}/.local/bin:$PATH" EXPOSE 8800 RUN echo "Configuring Python environment" \ && curl https://pyenv.run | bash \ && pyenv install ${PYTHON_VERSION} \ && pyenv global ${PYTHON_VERSION} \ && curl -LsSf https://astral.sh/uv/install.sh | sh \ && pip install ruff semver toml ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "name": "uv-pyenv", "build": { "dockerfile": "Dockerfile" }, // "features": {}, // 👇 Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // 👇 Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "(cd /workspaces/parlant* && git config --global --add safe.directory $PWD && python ./scripts/initialize_repo.py)", // 👇 Configure tool-specific properties. "customizations": { "vscode": { "extensions": [ "alexkrechik.cucumberautocomplete", "charliermarsh.ruff", "github.remotehub", "github.vscode-github-actions", "GitHub.vscode-pull-request-github", "hbenl.vscode-test-explorer", "matangover.mypy", "ms-azuretools.vscode-docker", "ms-python.debugpy", "ms-python.python", "ms-python.vscode-pylance", "mutantdino.resourcemonitor", "njpwerner.autodocstring", "tamasfe.even-better-toml", "streetsidesoftware.code-spell-checker" ] } }, // 👇 Features to add to the Dev Container. More info: https://containers.dev/implementors/features. "features": { "ghcr.io/devcontainers-extra/features/mypy:2": { "version": "latest" }, "node": { "version": "lts", "nodeGypDependencies": true } }, "mounts": [], // 👇 Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" "containerEnv": { "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" }, "remoteEnv": { "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" } } ================================================ FILE: .githooks/pre-commit ================================================ #!/bin/bash ROOT=$(git rev-parse --show-toplevel) cd $ROOT python scripts/lint.py --ruff ================================================ FILE: .githooks/pre-push ================================================ #!/bin/bash ROOT=$(git rev-parse --show-toplevel) cd $ROOT python scripts/lint.py --mypy --ruff ================================================ FILE: .githooks/prepare-commit-msg ================================================ #!/bin/bash # A git commit hook that will automatically append a DCO signoff to the bottom # of any commit message that does not have one. This append happens after the git # default message is generated, but before the user is dropped into the commit # message editor. ROOT=$(git rev-parse --show-toplevel) cd $ROOT COMMIT_MESSAGE_FILE="$1" AUTHOR=$(git var GIT_AUTHOR_IDENT) SIGNOFF=$(echo "$AUTHOR" | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # Check for DCO signoff message. If one does not exist, append one and then warn # the user that you did so. if ! grep -qs "^$SIGNOFF" "$COMMIT_MESSAGE_FILE"; then echo -e "\n$SIGNOFF" >> "$COMMIT_MESSAGE_FILE" echo -e "Appended the following signoff to the end of the commit message:\n $SIGNOFF\n" fi ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.md ================================================ --- name: Bug Report about: Create a report to help us improve title: "[Bug] " labels: bug assignees: '' --- # Description A clear and concise description of what the bug is. # How to Reproduce Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error # Expected Behavior A clear and concise description of what you expected to happen. # Environment - OS: [e.g. iOS] - Python version [e.g. 3.12] - Parlant version [e.g. 1.5.1] # Discussion Add any other context or open questions about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.md ================================================ --- name: Feature Request about: Suggest an idea for this project title: "[Enhancement] " labels: enhancement assignees: '' --- # Motivation A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] # Solution Proposal A clear and concise description of what you want to happen. # Discussion Add any other context or open questions about the feature request here. ================================================ FILE: .github/dco.yml ================================================ allowRemediationCommits: individual: true require: members: false ================================================ FILE: .github/workflows/ci-test.yml ================================================ name: Verify and Test on: push: branches: [ "main" ] pull_request: branches: [ "develop" ] jobs: build: runs-on: ubuntu-24.04 strategy: matrix: python-version: ["3.10"] steps: - name: checkout branch commit uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install uv run: pip install uv - name: Initial Configs run: | git config --local core.hooksPath .githooks/ chmod +x .githooks/pre-commit .githooks/pre-push - name: Install packages run: python scripts/install_packages.py - name: install just uses: extractions/setup-just@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Test Parlant (deterministic) if: always() env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: just test-deterministic - name: Test Parlant (core-stable) if: always() env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: just test-core-stable - name: Test Parlant (core-unstable) if: always() env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: just test-core-unstable - name: test log artifacts if: always() uses: actions/upload-artifact@v4 with: name: testresults path: logs/* ================================================ FILE: .github/workflows/docker-publish.yml ================================================ name: Docker # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. on: schedule: - cron: '44 23 * * *' push: branches: [ "develop" ] # Publish semver tags as releases. tags: [ 'v*.*.*' ] pull_request: branches: [ "develop" ] env: # Use docker.io for Docker Hub if empty REGISTRY: ghcr.io # github.repository as / IMAGE_NAME: emcie-co/parlant jobs: build: runs-on: ubuntu-24.04 permissions: contents: read packages: write # This is used to complete the identity challenge # with sigstore/fulcio when running outside of PRs. id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 # Install the cosign tool except on PR # https://github.com/sigstore/cosign-installer - name: Install cosign if: github.event_name != 'pull_request' uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 with: cosign-release: 'v2.2.4' # Set up BuildKit Docker container builder to be able to build # multi-platform images and export cache # https://github.com/docker/setup-buildx-action - name: Set up Docker Buildx uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 # Login against a Docker registry except on PR # https://github.com/docker/login-action - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # Extract metadata (tags, labels) for Docker # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=raw,value=edge # Remove unused packages and directories to free up space for a (possibly large) Docker build - name: Cleanup disk space run: | chmod +x ./scripts/ci/github_action_ubuntu_2404_free_space.sh ./scripts/ci/github_action_ubuntu_2404_free_space.sh # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: context: . file: ./Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max # Sign the resulting Docker image digest except on PRs. # This will only write to the public Rekor transparency log when the Docker # repository is public to avoid leaking data. If you would like to publish # transparency data even for private images, pass --force to cosign below. # https://github.com/sigstore/cosign - name: Sign the published Docker image if: ${{ github.event_name != 'pull_request' }} env: # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable TAGS: ${{ steps.meta.outputs.tags }} DIGEST: ${{ steps.build-and-push.outputs.digest }} # This step uses the identity token to provision an ephemeral certificate # against the sigstore community Fulcio instance. run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} ================================================ FILE: .github/workflows/lint.yml ================================================ name: Lint on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: build: runs-on: ubuntu-24.04 strategy: matrix: python-version: ["3.10"] steps: - name: checkout branch commit uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install uv run: pip install uv - name: Initial Configs run: | git config --local core.hooksPath .githooks/ chmod +x .githooks/pre-commit .githooks/pre-push - name: Install packages run: python scripts/install_packages.py continue-on-error: false - name: Lint packages run: python scripts/lint.py continue-on-error: false ================================================ FILE: .gitignore ================================================ .env .pytest_cache .mypy_cache .ruff_cache __pycache__ .justfile testresults** tests/core/persistence/test_cache/*.json data/*.json cache/ .coverage .DS_STORE *~ .vscode .venv .cursor logs runtime-data parlant-data /dist/ scripts/sdks/ scripts/fern/openapi fern.generate.log schematic_generation_test_cache.json test_timing.csv ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to Parlant will be documented here. ## [Unreleased] ### Changed - Change tag dependency semantics from ALL to ANY: a dependency on a tag is now satisfied when at least one tagged member (guideline, observation, or journey) is active, rather than requiring all of them ### Fixed - Fix `Variable.get_value()` returning `None` when called from a retriever, caused by retrievers starting before context variables were loaded ## [3.3.0] - 2025-03-15 ### Added - Add per-agent planners via `Server.create_agent(planner=...)`, allowing each agent to use a custom `Planner` implementation - Accept `Tag` as a target in `depend_on()`, `exclude()`, and `prioritize_over()` on both `Guideline` and `Tag`, enabling relationships that target all guidelines sharing a custom tag - Add `Tag.depend_on()`, `Tag.exclude()`, and `Tag.prioritize_over()` methods to the SDK, enabling tag-based dependency and priority relationships with guidelines and journeys - Support custom TAG as source for DEPENDENCY relationships in the relational resolver - Add `tags` parameter to `create_guideline`, `create_observation`, and `create_journey` on both `Agent` and `Journey`, allowing custom tags to be attached to entities at creation time - Add `Tag.reevaluate_after()` method to the SDK, enabling tag-based reevaluation relationships with tools - Add tag-based reevaluation support in the engine: when a tool fires, all guidelines carrying a tag that has a reevaluation relationship with that tool are now re-evaluated - Add staged_events to GuidelineMatchingContext in SDK - Add `priority` property to guidelines and journeys for priority-based filtering in the relational resolver - Add transient guidelines (renamed from tool-provided guidelines), allowing tools to dynamically inject behavioral guidelines into the agent's context - Add `Agent.utter()` to the SDK, enabling programmatic agent message generation with transient guidelines - Add `Customer.update()` and `CustomerMetadata` to the SDK, allowing tools to update customer name and metadata - Add `Session.update()`, `SessionMetadata`, and `SessionLabels` to the SDK, allowing tools to update session properties, metadata, and labels - Add `customer`, `agent`, `mode`, and `title` properties to SDK `Session` class - Add `Server.get_tag()` to the SDK, supporting lookup by either `id` or `name` - Add name-based filtering to `TagStore.list_tags()` and the `GET /tags` API endpoint via an optional `name` query parameter - Enforce tag name uniqueness in `TagStore`, raising an error when creating a tag with a duplicate name ### Changed - Made extended thinking indicator optional in perceived performance policy - Change `reevaluate_after()` on `Tag` and `Guideline` to accept multiple tools (`*tools`) and return `Sequence[Relationship]` - Change `tags` field type from `Sequence[TagId]` to `Sequence[Tag]` on `Guideline`, `Journey`, `Capability`, `Term`, `Variable`, `Customer`, and `Agent` in the SDK - Change `Tag.preamble()` to return a full `Tag` object instead of a `TagId` - Upgrade MCP service and bump dependency versions to resolve security vulnerabilities ### Deprecated - OpenAPI tool services are now deprecated; please migrate to SDK tool services ### Fixed - Fix deadlock when sending a new message right after a preamble - Fix transitive filtering in relational resolver for custom tag dependency targets (guidelines depending on a custom tag are now correctly deactivated when a tagged member is deprioritized) - Fix SSE `read_event` endpoint stalling after first streaming chunk until full completion - Fix response analysis logs not always reaching the integrated UI - Fix guideline formatting in canned response and streaming modes when condition is absent - Fix AzureService small text embedding dimension size - Fix onnxruntime compatibility with Python 3.10 and transformers 5.x type changes - Fix agent intention proposer prompt clarification - Fix embedding LRU cache eviction corrupting the length index when entries share the same text length - Fix LiteLLMEmbedder failing to resolve via lagom container when LITELLM_EMBEDDING_MODEL_NAME is set - Fix non-consequential tool calls being rejected when optional parameters are missing ### Removed - Remove stale `parlant-test` entry point and testing framework documentation from README ## [3.2.2] - 2025-02-18 ### Added - Added p.MATCH_ALWAYS, now the preferred alias to p.Guideline.MATCH_ALWAYS - Added `logger` property to p.Server ### Changed - Adjusted log levels of relational resolver to trace instead of debug - Allow tool context parameter names to be all of 'context', 'ctx', and 'c' ### Fixed - Fix completed streamed messages re-animating on page refresh - Propagate `Server.current` context to tool functions in hosted plugin server ## [3.2.1] - 2026-02-17 ### Added - Add optional `dependencies` parameter to guideline, observation, and journey creation methods - Add `exclude()` as an alias for `prioritize_over()` on guidelines and journeys - Add `tools` parameter to `create_observation` methods ### Changed - Deprecate `attach_tool()` in favor of `create_guideline()`/`create_observation()` with `tools` parameter ### Fixed - Preserve draft message language during canned response recomposition - Fix server hang when an exception occurs during setup - Fix canned response field extraction to handle falsy values ## [3.2.0] - 2026-02-08 ### Added - Add labels to Guidelines, Journeys, JourneyNodes, and Sessions for categorization and filtering - Add automatic session label propagation from matched entities (guidelines, observations, journeys) - Add `track` parameter to guidelines to control "previously applied" tracking - Support multiple targets in `prioritize_over()` and `depend_on()` methods - Add `field_dependencies` to canned responses for explicit field availability requirements - Add `attach_retriever()` to Guideline, Journey, and JourneyState for conditional data retrieval - Add `on_match` and `on_message` hooks to journeys for lifecycle callbacks - Add per-agent preamble configuration (custom examples and instructions) - Add separate default greeting responses for first agent message in fluid mode - Add streaming message output mode - Allow specifying custom journey node ID - Add matched guidelines/journey states to completion ready event ### Changed - Make condition optional for SDK guidelines - Tweak default preamble examples - Soften log levels for relational guideline resolver - Add activated/skipped logs to custom guideline matcher batches ### Fixed - Fix websocket warning upon startup - Fix agent intention proposer (guidelines were getting rewritten incorrectly) - Fix multiple customer guideline matchers not working - Fix bug with context variable access in SDK ## [3.1.0] - 2026-01-05 ### Added - Add .current property for Server, Agent, and Customer in SDK - Add /healthz endpoint - Add API for CRUD operations on session metadata - Add EmcieService - Add GLM service - Add Mistral service - Add OpenRouter service - Add OpenTelemetry integration for Meter, Logger and Tracer - Add Qdrant VectorDatabase adapter - Add Snowflake Cortex service - Add ability to configure and extend the FastAPI app object - Add deferred retrievers - Add dynamic composition mode - Add follow-up canned responses - Add guideline criticality level - Add guideline on_match() hooks - Add persistence option for context variable values (variable store) - Added guideline descriptions - Allow bailing out of canned response selection and utilize the draft directly, using a hook - Allow controlling max tool result payload via environment variable - Allow controlling perceived performance policy per agent - Allow journey transitions from one tool state to another - Allow specifying custom IDs when creating agents via SDK and API - Allow specifying custom IDs when creating customers via SDK and API - Allow specifying custom IDs when creating guidelines, journeys, and glossary terms via SDK and API - Expose IoC container in server object - Support adding custom canrep fields to matched guidelines and journey states - Support code-based, custom guideline matchers ### Changed - Changed default NLPService to EmcieService - Improved efficiency of journey state matching when first state is a tool state - Rename ContextualCorrelator to Tracer - Rename LoadedContext to EngineContext - Support proxy URL for LiteLLM ### Fixed - Fix critical bug with cancellation during response analysis - Fix critical similarity calculation error in TransientVectorDatabase - Fix unnecessary extra evaluation of journeys and tools in some edge cases - Improved Gemini Flash 2.5 output consistency by using function call trick instead of structured outputs ## [3.0.4] - 2025-11-18 ### Fixed - Fix bug where NanoDB query failed when no filters matched - Extend tool insights across iterations - Fix deprecated status.HTTP_422_UNPROCESSABLE_ENTITY to status.HTTP_422_UNPROCESSABLE_CONTENT - Fix broken CLI by adding missing websocket-client dependency - Added specific classes for embedder initialisation - Make base url once in OllamaEmbedder - Update dependencies for security, upgrade FastAPI, fix mypy in hugging_face.py - Bump torch for fixing vulnerability ## [3.0.3] - 2025-10-23 ### Fixed - Fix installation issue in some environments, failing due to an older FastMCP version - Bump versions of OpenTelemetry - Made ChromaDB an extra package parlant[chroma] - Update NPM dependencies for integrated UI ## [3.0.2] - 2025-08-27 ### Added - Added docs/\* and llms.txt - Added Vertex NLP service - Added Ollama NLP service - Added LiteLLM support to the SDK - Added Gemini support to the SDK - Added Journey.create_observation() helper - Added auth permission READ_AGENT_DESCRIPTION - Added optional AWS_SESSION_TOKEN to BedrockService - Support creating status events via the API ### Changed - Moved tool call success log to DEBUG level - Optimized canrep to not generate a draft in strict mode if no canrep candidates found - Removed `acknowledged_event_offset` from status events - Removed `last_known_event_offset` from `LoadedContext.interaction` ### Fixed - Fixed presentation of missing API keys for built-in NLP services - Improvements to canned response generation - Fixed bug with null journey paths in some cases - Fixed tiny bug with terminal nodes in journey node selection - Fixed evaluations not showing properly after version upgrade ## [3.0.1] - 2025-08-16 ### Changed - Move tool call success log to DEBUG level ### Fixed - Fix tool-based variable not enabling the associated tool on the server - Fix authorization errors throwing 500 instead of 403 - Changed OpenAI LLM request operation level to TRACE to fix evaluation progress bars ## [3.0.0] - 2025-08-15 - Please see the announcement at https://parlant.io/blog/parlant-3-0-release ## [2.2.0] - 2025-05-20 ### Added - Add journeys - Add of guideline properties evaluation - Add automatic guideline action deduction when adding direct tool guidelines - Added choices of invalid and missing tool parameters to tool insights ### Changed - Make guideline action optional ## [2.1.2] - 2025-05-07 ### Changed - Remove interaction history from utterance recomposition prompt - Use tool calls from the entire interaction for utterance field substitution - Improve error handling and reporting with utterance rendering failures ### Fixed - Always reason about utterance selection to improve performance ## [2.1.1] - 2025-04-30 ### Fixed - Fixed rendering relationships in CLI - Fixed parlant client using old imports from python client SDK ## [2.1.0] - 2025-04-29 ### Added - ToolParameterOptions.choice_provider can now access ToolContext - Added utterance/draft toggle in the integrated UI - Added new guideline relationship: Dependency - Added tool relationships and the OVERLAP relationship - Added the 'overlap' property to tools. By default, tools will be assumed not to overlap with each other, simplifying their evaluation at runtime. - Introduce ToolBatchers - Introduce Journey ### Changed - Improved tool calling efficiency by adjusting the prompt to the tool at hand - Revised completion schema (ARQs) for tool calling - Utterances now follow a 2-stage process: draft + select - Changed guest customer name to Guest ### Fixed - Fixed deprioritized guidelines always being skipped - Fixed agent creation with tags - Fixed client CLI exit status when encountering an error - Fixed agent update ### Known Issues - OpenAPI tool services sometimes run into issues due to a version update in aiopenapi3 ## [2.0.0] - 2025-04-09 ### Added - Improved tool parameter flexibility: custom types, Pydantic models, and annotated ToolParameterOptions - Allow returning a new (modified) container in modules using configure_module() - Added Tool Insights with tool parameter options - Added support for default values for tool parameters in tool calling - Added WebSocket logger feature for streaming logs in real time - Added a log viewer to the sandbox UI - Added API and CLI for Utterances - Added support for the --migrate CLI flag to enable seamless store version upgrades during server startup - Added clear rate limit error logs for NLP adapters - Added enabled/disabled flag for guidelines to facilitate experimentation without deletion - Allow different schematic generators to adjust incoming prompts in a structured manner - Added tags to context variables, guidelines, glossary and agents - Added guideline matching strategies - Added guideline relationships - Added support for tool parameters choice provider using the tool context as argument ### Changed - Made the message generator slightly more polite by default, following user feedback - Allow only specifying guideline condition or action when updating guideline from CLI - Renamed guideline proposer with guideline matcher ### Fixed - Lowered likelihood of the agent hallucinating facts in fluid mode - Lowered likelihood of the agent offering services that were not specifically mentioned by the business ## [1.6.2] - 2025-01-29 ### Fixed - Fix loading DeepSeek service during server boot ## [1.6.1] - 2025-01-20 ### Fixed - Fix ToolCaller not getting clear information on a parameter being optional - Ensure ToolCaller only calls a tool if all required args were given - Improve valid JSON generation likelihood in MessageEventGenerator - Improve ToolCaller's ability to correctly run multiple tools at once ## [1.6.0] - 2025-01-19 ### Added - Add shot creation helper functions under Shot - Add ContextEvaluation in MessageEventGenerator - Add a log command under client CLI for streaming logs - Add engine lifecycle hooks ### Changed - Split vendor dependencies to extra packages to avoid reduce installation time - Modified ToolCaller shot schema - Disable coherence and connection checking by default in the CLI for now ### Fixed - Improved GuidelineProposer's ability to handle compound actions - Improved GuidelineProposer's ability to distinguish between a fulfilled and unfulfilled action - Improved GuidelineProposer's ability to detect a previously applied guideline's application to new information - Reduced likelihood of agent offering hallucinated services - Fix ToolCaller false-negative argument validation from int to float - Fix ToolCaller accuracy - Fix ToolCaller making up argument values when it doesn't have them - Fix some cases where the ToolCaller also calls a less-fitting tool - Fix mistake in coherence checker few shots - Fix markdown tables in sandbox UI - Fix wrong import of RateLimitError - Fix PluginServer validation for optional tool arguments when they're passed None - Fix utterances sometimes not producing a message ## [1.5.1] - 2025-01-05 ### Fixed - Fix server CLI boot ## [1.5.1] - 2025-01-05 ### Fixed - Fix server CLI boot ## [1.5.0] - 2025-01-04 ### Added - Add DeepSeek provider support (via DeepSeekService) ### Changed - Change default home dir from runtime-data to parlant-data ### Fixed - Fix tool-calling test - Fix HuggingFace model loading issues ## [1.4.3] - 2025-01-02 ### Fixed - Upgraded dependency "tiktoken" to 0.8.0 to fix installation errors on some environments ## [1.4.2] - 2024-12-31 ### Fixed - Fix race condition in JSONFileDocumentDatabase when deleting or updating documents ## [1.4.1] - 2024-12-31 ### Changed - Remove tool metadata from prompts - agents are now only aware of the data itself ### Fixed - Fix tool calling in scenarios where a guideline has multiple tools where more than one should run ## [1.4.0] - 2024-12-31 ### Added - Support custom plugin data for PluginServer - Allow specifying custom logger ID when creating loggers - Add 'hosted' parameter to PluginServer, for running inside modules ### Fixed - Fix the tool caller's few shots to include better rationales and arguments. ## [1.3.1] - 2024-12-27 ### Changed - Return event ID instead of trace ID from utterance API - Improve and normalize entity update messages in client CLI ## [1.3.0] - 2024-12-26 ### Added - Add manual utterance requests - Refactor few-shot examples and allow adding more examples from a module - Allow tapping into the PluginServer FastAPI app to provide additional custom endpoints - Support for union parameters ("T | None") in tool functions ### Changed - Made all stores thread-safe with reader/writer locks - Reverted GPT version for guideline connection proposer to 2024-08-06 - Changed definition of causal connection to take the source's when statement into account. The connection proposer now assumes the source's condition is true when examining if it entails other guideline. ### Fixed - Fix 404 not being returned if a tool service isn't found - Fix having direct calls to asyncio.gather() instead of safe_gather() ### Removed - Removed connection kind (entails / suggests) from the guideline connection proposer and all places downstream. the connection_kind argument is no longer needed or supported for all guideline connections. ## [1.2.0] - 2024-12-19 ### Added - Expose deletion flag for events in Session API ### Changed - Print traceback when reporting server boot errors - Make cancelled operations issue a warning rather than an error ### Fixed - Fixed tool calling with optional parameters - Fixed sandbox UI issues with message regeneration and status icon - Fixed case where guideline is applied due to condition being partially applied ### Removed None ## [1.1.0] - 2024-12-18 ### Added - Customer selection in sandbox Chat UI - Support tool calls with freshness rules for context variables - Add support for loading external modules for changing engine behavior programmatically - CachedSchematicGenerator to run the test suite more quickly - TransientVectorDatabase to run the test suite more quickly ### Changed - Changed model path for Chroma documents. You may need to delete your `runtime-data` dir. ### Fixed - Improve handling of partially fulfilled guidelines ### Removed None ================================================ FILE: CLAUDE.md ================================================ This is the main repo of Parlant (https://parlant.io). Parlant is a Python based agent framework. Its core strengths: 1. It allows you to create compliant and controlled AI agents for customer-facing use cases 2. It provides many conversational management features out of the box 3. It's built for enterprise, large-scale use cases, where SLAs, stability and security are paramount The repo's structure follows the Hexagonal Architecture (Ports and Adapters) approach. - src/parlant - core: Core framework code - adapters: Implementations of interfaces using 3rd party tools - api: REST API layer using FastAPI. Uses modules from core/ - tests: all tests for the project. Structure strives to mirror that which is under src/parlant. General Coding Instructions: - Always ensure you stick to Hexagonal Architecture patterns in line with how they're used in this codebase. - Every time you add something, look for similar things in the codebase and ensure you follow the coding style. - We use MyPy on strict mode. Every parameter needs to be type-annotated. Every function's result too. - If you need to add a test for something, first say where you plan to add it and ask for confirmation. - We follow TDD. When you make a change, first create a failing test. Once it fails, implement just enough so it passes. - If you need to test classes/methods in sdk.py (or generally to test things that relate to engine behavior) make sure you inherit from SDKTest and understand how it works and how to use it. - Test names should go "test*that*..." using clear names that explain the context, what is executed, and what is the expected result. - You can run tests using pytest. Make sure you run "uv run pytest tests/path/to/test/file.py" while also specifying the test name that you need to run. Always follow this plan when asked to code a feature or fix a bug: 1. Consider the codebase's structure 2. Describe your implementation plan, including: a. What tests you will write (test names + files they would live in) b. Why do you think the tests would initially fail c. Where you would plan to implement the code that would make the tests pass 3. Ask for plan confirmation. If you get feedback, revise your plan and ask for confirmation again until you get it. 4. Implement the tests first. Ask for confirmation and code review. 5. Once tests are approved, once again suggest your implementation plan for making them pass, and get plan review until confirmation. 6. Once your implementation plan is confirmed, go ahead with implementing the code to pass them. 7. Make sure to format all of the files you changed using ruff (it is installed in the environment). 8. Run `uv run python scripts/lint.py --mypy --ruff` to ensure your code has no lint issues. ================================================ FILE: CONTRIBUTING.md ================================================ # DCO Sign Off All commits must be signed off with the Developer Certificate of Origin ([DCO.md](DCO.md)). This attests that you have the rights to submit your contribution under our project's license (Apache 2.0). To sign off your commits: 1. Configure your Git client with your github account details: ``` git config --global user.name "Your Name" git config --global user.email "your.email@example.com" ``` 2. If you've configured git to use our hooks (`.githooks`), you are now ready. Otherwise, either: 1. use our `.githooks`: ``` git config set core.hookspath .githooks ``` **OR** 2. Add the `-s` flag when committing: ``` git commit -s -m "Your commit message" ``` ### Or * Add the sign-off manually with: ``` Signed-off-by: Your Name ``` ================================================ FILE: DCO.md ================================================ Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2025 Emcie Co Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================
Parlant ### The conversational control layer for customer-facing AI agents

PyPI Python 3.10+ License Discord GitHub Repo stars

WebsiteQuick StartExamplesDiscord

Deutsch | Español | français | 日本語 | 한국어 | Português | Русский | 中文

Trending
  **Parlant streamlines conversational context engineering for enterprise-grade B2C (business to consumer) and sensitive B2B interactions that need to be consistent, compliant, and on-brand.** ## Why Parlant? Conversational context engineering is hard because real-world interactions are diverse, nuanced, and non-linear. ### ❌ The Problem: What you've probably tried and couldn't get to work at scale **System prompts** work until production complexity kicks in. The more instructions you add to a prompt, the faster your agent stops paying attention to any of them. **Routed graphs** solve the prompt-overload problem, but the more routing you add, the more fragile it becomes when faced with the chaos of natural interactions. ### 🔑 The Solution: Context engineering, optimized for conversational control Parlant solves this with [context engineering](https://www.gartner.com/en/articles/context-engineering): getting the right context, no more and no less, into the prompt at the right time. You define your rules, knowledge, and tools once; the engine narrows the context in real-time to what's immediately relevant to the current turn. Parlant Demo ## Getting started ```bash pip install parlant ``` ```python import parlant.sdk as p async with p.Server(): agent = await server.create_agent( name="Customer Support", description="Handles customer inquiries for an airline", ) # Evaluate and call tools only under the right conditions expert_customer = await agent.create_observation( condition="customer uses financial terminology like DTI or amortization", tools=[research_deep_answer], ) # When the expert observation holds, always respond # with depth. Set the guideline to automatically match # whenever the observation it depends on holds... expert_answers = await agent.create_guideline( matcher=p.MATCH_ALWAYS, action="respond with technical depth", dependencies=[expert_customer], ) beginner_answers = await agent.create_guideline( condition="customer seems new to the topic", action="simplify and use concrete examples", ) # When both match, beginners wins. Neither expert-level # tool-data nor instructions can enter the agent's context. await beginner_answers.exclude(expert_customer) ``` Follow the **[5-minute quickstart](https://www.parlant.io/docs/quickstart/installation)** for a full walkthrough. ## Parlant at a glance You define your agent's behavior in code (not prompts), and the engine dynamically narrows the context on each turn to only what's immediately relevant, so the LLM stays focused and your agent stays aligned. ```mermaid graph TD O[Observations] -->|Events| E[Contextual Matching Engine] G[Guidelines] -->|Instructions| E J["Journeys (SOPs)"] -->|Current Steps| E R[Retrievers] -->|Domain Knowledge| E GL[Glossary] -->|Domain Terms| E V[Variables] -->|Memories| E E -->|Tool Requests| T[Tool Caller] T -.->|Results + Optional Extra Matching Iterations| E T -->|**Key Result:**
Focused Context Window| M[Message Generation] ``` Instead of sending a large system prompt followed by a raw conversation to the model, Parlant first assembles a focused context — matching only the instructions and tools relevant to each conversational turn — then generates a response from that narrowed context. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e8f5e9', 'primaryTextColor': '#1b5e20', 'primaryBorderColor': '#81c784', 'lineColor': '#66bb6a', 'secondaryColor': '#fff9e1', 'tertiaryColor': 'transparent'}}}%% flowchart LR A(User):::outputNode subgraph Engine["Parlant Engine"] direction LR B["Match Guidelines and Resolve Journey States"]:::matchNode C["Call Contextually-Associated Tools and Workflows"]:::toolNode D["Generated Message"]:::composeNode E["Canned Message"]:::cannedNode end A a@-->|💬 User Input| B B b@--> C C c@-->|Fluid Output Mode?| D C d@-->|Strict Output Mode?| E D e@-->|💬 Fluid Output| A E f@-->|💬 Canned Output| A a@{animate: true} b@{animate: true} c@{animate: true} d@{animate: true} e@{animate: true} f@{animate: true} linkStyle 2 stroke-width:2px linkStyle 4 stroke-width:2px linkStyle 3 stroke-width:2px,stroke:#3949AB linkStyle 5 stroke-width:2px,stroke:#3949AB classDef composeNode fill:#F9E9CB,stroke:#AB8139,stroke-width:2px,color:#7E5E1A,stroke-width:0 classDef cannedNode fill:#DFE3F9,stroke:#3949AB,stroke-width:2px,color:#1a237e,stroke-width:0 ``` In this way, adding more rules makes the agent smarter, not more confused — because the engine filters context relevance, not the LLM. ## Is Parlant for you? Parlant is built for teams that need their AI agent to behave reliably in front of real customers. It's a good fit if: - You're building a **customer-facing agent** — support, sales, onboarding, advisory — where tone, accuracy, and compliance matter. - You have **dozens or hundreds of behavioral rules** and your system prompt is buckling under the weight. - You're in a **regulated or high-stakes domain** (finance, insurance, healthcare, telecom) where every response needs to be explainable and auditable. **_Parlant is deployed in production at the most stringent organizations, including banks._** > _Parlant isn't just a framework. It's a high-level software that solves the conversational modeling problem head-on._ > — **Sarthak Dalabehera**, Principal Engineer, Slice Bank > _By far the most elegant conversational AI framework that I've come across._ > — **Vishal Ahuja**, Senior Lead, Applied AI, JPMorgan Chase > _Parlant dramatically reduces the need for prompt engineering and complex flow control. Building agents becomes closer to domain modeling._ > — **Diogo Santiago**, AI Engineer, Orcale ## Features - **[Guidelines](https://parlant.io/docs/concepts/customization/guidelines)** — Behavioral rules as condition-action pairs; the engine matches only what's relevant per turn. - **[Relationships](https://parlant.io/docs/concepts/customization/relationships)** — Dependencies and exclusions between guidelines to keep the context narrow and focused. - **[Journeys](https://parlant.io/docs/concepts/customization/journeys)** — Multi-turn SOPs that adapt to how the customer actually interacts. - **[Canned Responses](https://parlant.io/docs/concepts/customization/canned-responses)** — Pre-approved response templates that eliminate hallucination at critical moments. - **[Tools](https://parlant.io/docs/concepts/customization/tools)** — External APIs and workflows, triggered only when their observation matches. - **[Glossary](https://parlant.io/docs/concepts/customization/glossary)** — Domain-specific vocabulary so the agent understands customer language. - **[Explainability](https://parlant.io/docs/advanced/explainability)** — Full OpenTelemetry tracing — every guideline match and decision is logged. ## [Guidelines](https://parlant.io/docs/concepts/customization/guidelines) Behavioral rules as condition-action pairs: when the condition applies, the action kicks into context. Instead of cramming all guidelines in a single prompt, the engine evaluates which ones apply on each conversational turn and only includes the relevant ones in the LLM's context. This lets you define hundreds of guidelines without degrading adherence. ```python await agent.create_guideline( condition="customer uses financial terminology like DTI or amortization", action="respond with technical depth — skip basic explanations", ) ``` ## [Relationships](https://parlant.io/docs/concepts/customization/guidelines) Relationships between elements help you keep the final context just right: narrow and focused. **Exclusion** relationships keep certain guidelines out of the model's attention when conflicting ones are matched. ```python for_experts = await agent.create_guideline( condition="customer uses financial terminology", action="respond with technical depth", ) for_beginners = await agent.create_guideline( condition="customer seems new to the topic", action="simplify and use concrete examples", ) # In conflicting reads of the customer, set which takes priority await for_beginners.exclude(for_experts) ``` **Dependency** relationships ensure a guideline only activates when another one has set the stage, helping you create _topic-based guideline hierarchies._ ```python suspects_fraud = await agent.create_observation( condition="customer suspects unauthorized transactions on their card", ) await agent.create_guideline( condition="customer wants to take action regarding the transaction", action="ask whether they want to dispute the transaction or lock the card", # Only activates when fraud suspicion has been established dependencies=[suspects_fraud], ) ``` ## [Journeys](https://parlant.io/docs/concepts/customization/journeys) Multi-turn SOPs (Standard Operating Procedures). Define a flow for processes like booking, troubleshooting, or onboarding. The agent follows the flow but adapts — it can fast-forward states, revisit earlier ones, or adjust pace based on how the customer interacts. ```python journey = await agent.create_journey( title="Book Flight", description="Guide the customer through flight booking", conditions=["customer wants to book a flight"], ) t0 = await journey.initial_state.transition_to( # Instruction to follow while in this state (could be multiple turns) chat_state="See if they're interested in last-minute deals", ) # Branch A - not interested in deals t1 = await t0.target.transition_to( chat_state="Determine where they want to go and when", condition="They aren't interested", ) # Branch B - interested in deals t2 = await t0.target.transition_to( tool_state=load_latest_flight_deals, condition="They are", ) t3 = await t1.target.transition_to( chat_state="List deals and see if they're interested", ) ``` ## [Canned Responses](https://parlant.io/docs/concepts/customization/canned-responses) At critical moments or conversational events, limit the agent to using only pre-approved response templates. After running the matching sequence and drafting a message to the customer, the agent selects the template that best matches its generated draft instead of sending it directly, eliminating hallucination risk entirely and keeping wording exact to the letter. ```python await agent.create_guideline( condition="The customer discusses things unrelated to our business" action="Tell them you can't help with that", # Strict composition mode triggers when this guideline # matches - the rest of the agent stays fluid composition_mode=p.CompositionMode.STRICT, canned_responses=[ await agent.create_canned_response( "Sorry, but I can't help you with that." ) ], priority=100, # Top priority, focuses the agent on this alone ) ``` ## [Tools](https://parlant.io/docs/concepts/customization/tools) Tools activate only when their observation matches; they don't sit in the context permanently. This prevents the false-positive invocations that plague traditional LLM tool setups. ```python @p.tool async def query_docs(context: p.ToolContext, user_query: str) -> p.ToolResult: results = search_knowledge_base(user_query) return p.ToolResult(results) await agent.create_observation( condition="customer asks about service features", tools=[query_docs], ) ``` Tools can also feed custom values into canned response templates. ## [Glossary](https://parlant.io/docs/concepts/customization/glossary) Domain-specific vocabulary for your agent. Map colloquial terms and synonyms to precise business definitions so the agent understands customer language. ```python await agent.create_term( name="Ocean View", description="Room category with direct view of the Atlantic", synonyms=["sea view", "rooms with a view to the Atlantic"], ) ``` ## [Explainability](https://parlant.io/docs/advanced/explainability) Every decision is traced with OpenTelemetry. Parlant ships out of the box with elaborate logs, metrics, and traces. ## Framework Integration Parlant handles conversational governance; it doesn't replace your existing stack. Use it alongside frameworks like LangGraph, Agno, LlamaIndex, or others for workflow automation and knowledge retrieval. Parlant takes over the behavioral control layer while your framework of choice handles the rest of your agent's processing logic. Any external workflow or agent becomes a Parlant tool, triggered only when relevant: ```python from my_workflows import refund_graph # a compiled LangGraph StateGraph @p.tool async def run_refund_workflow( context: p.ToolContext, order_id: str ) -> p.ToolResult: result = await refund_graph.ainvoke({"order_id": order_id}) # Graph result can inject both data and instructions into the agent. # Instructions are transformed to guidelines, and participate # in contextual guideline resolution (including prioritizations) return p.ToolResult( data=result["data"], # Inject dynamic guidelines from workflow result guidelines=[ {"action": inst, "priority": 3} for inst in result["instructions"] ], ) await agent.create_observation( condition="customer wants to process a refund", tools=[run_refund_workflow], ) ``` The same pattern works with LlamaIndex query engines, Agno agents, or any async Python function. ## LLM Agnostic Parlant works with most LLM providers. The recommended ones are [Emcie](https://www.emcie.co) which delivers an ideal cost/quality value since it's built specifically for Parlant, but OpenAI and Anthropic deliver excellent quality outputs as well. You can also use any model and provider via LiteLLM, but they need to be good ones - off-the-shelf models which are too small tend to produce inconsistent results. Generally, you can swap models without changing behavioral configuration. ## [Official React Chat Widget](https://github.com/emcie-co/parlant-chat-react) Drop-in chat component to get a frontend running immediately. ## Learn more - **[How Parlant ensures compliance](https://www.parlant.io/blog/how-parlant-guarantees-compliance)** — deep dive into the engine - **[Parlant vs LangGraph](https://www.parlant.io/blog/parlant-vs-langgraph)** — when to use which - **[Parlant vs DSPy](https://www.parlant.io/blog/parlant-vs-dspy)** — different tools for different problems ## Community - **[Discord](https://discord.gg/duxWqxKk6J)** — ask questions, share what you're building - **[GitHub Issues](https://github.com/emcie-co/parlant/issues)** — bug reports and feature requests - **[Contact](https://parlant.io/contact)** — reach the engineering team directly **If Parlant helps you build better agents, **[give it a star](https://github.com/emcie-co/parlant)** — it helps others find the project.** ## License Apache 2.0 — free for commercial use. ---
**[Try it now](https://www.parlant.io/docs/quickstart/installation)** • **[Join Discord](https://discord.gg/duxWqxKk6J)** • **[Read the docs](https://www.parlant.io/)** Built by the team at **[Emcie](https://emcie.co)**
================================================ FILE: docs/adapters/nlp/azure.md ================================================ # Azure OpenAI Service Documentation The Azure service provides integration with Azure OpenAI services, supporting both legacy API key authentication and modern Azure AD authentication. This integration enables Parlant to leverage Azure's enterprise-grade AI services while maintaining security best practices. ## Prerequisites 1. **Azure OpenAI Resource**: Create an Azure OpenAI resource in your Azure subscription 2. **Authentication Setup**: Choose between API key or Azure AD authentication 3. **Model Deployment**: Deploy required models in your Azure OpenAI resource 4. **Permissions**: Ensure proper IAM roles for Azure AD authentication ## Authentication Methods ### Development (Local Machine) For local development, use Azure CLI authentication: ```bash # Install Azure CLI if not already installed # https://docs.microsoft.com/en-us/cli/azure/install-azure-cli # Login to Azure az login # Set your endpoint export AZURE_ENDPOINT="https://your-resource.openai.azure.com/" ``` ### Production (Server Deployment) For server deployment, **do NOT use `az login`**. Instead, use one of these methods: #### Option 1: Service Principal (Recommended) ```bash # Set environment variables export AZURE_ENDPOINT="https://your-resource.openai.azure.com/" export AZURE_CLIENT_ID="your-service-principal-client-id" export AZURE_CLIENT_SECRET="your-service-principal-secret" export AZURE_TENANT_ID="your-azure-tenant-id" ``` #### Option 2: Managed Identity (Azure Resources) If running on Azure VMs, App Services, or other Azure resources: ```bash # Only set the endpoint - authentication is automatic export AZURE_ENDPOINT="https://your-resource.openai.azure.com/" ``` #### Option 3: Workload Identity (Kubernetes) For Kubernetes deployments: ```bash export AZURE_ENDPOINT="https://your-resource.openai.azure.com/" export AZURE_CLIENT_ID="your-workload-identity-client-id" export AZURE_TENANT_ID="your-azure-tenant-id" export AZURE_FEDERATED_TOKEN_FILE="/var/run/secrets/azure/tokens/azure-identity-token" ``` ## Environment Variables ### Required Variables - `AZURE_ENDPOINT`: Your Azure OpenAI resource endpoint ### Optional Variables - `AZURE_API_VERSION`: API version (default: "2024-08-01-preview") - `AZURE_GENERATIVE_MODEL_NAME`: Model name (default: "gpt-4o") - `AZURE_GENERATIVE_MODEL_WINDOW`: Context window size (default: 4096) - `AZURE_EMBEDDING_MODEL_NAME`: Embedding model (default: "text-embedding-3-large") - `AZURE_EMBEDDING_MODEL_DIMS`: Embedding dimensions (default: 3072) - `AZURE_EMBEDDING_MODEL_WINDOW`: Embedding context window (default: 8192) ## Supported Models The Azure service supports **any Azure OpenAI model** that is deployed and available in your Azure OpenAI resource. The models listed below are pre-configured defaults, but you can use any model by setting the appropriate environment variables. ### Pre-configured Generative Models | Model Name | Description | Context Window | Use Case | |------------|-------------|---------------|----------| | `gpt-4o` | Most capable GPT-4 model (default) | 128K tokens | Complex reasoning, high accuracy | | `gpt-4o-mini` | Faster, cost-effective GPT-4 | 128K tokens | Balanced performance and cost | ### Pre-configured Embedding Models | Model Name | Dimensions | Context Window | Description | |------------|------------|---------------|-------------| | `text-embedding-3-large` | 3072 | 8192 | High-quality embeddings (default) | | `text-embedding-3-small` | 3072 | 8192 | Efficient embeddings | ### Using Custom Models You can use **any Azure OpenAI model** that is deployed in your Azure OpenAI resource: ```bash # Use any generative model (examples - check your Azure resource for availability) export AZURE_GENERATIVE_MODEL_NAME="gpt-35-turbo" # GPT-3.5 Turbo export AZURE_GENERATIVE_MODEL_NAME="gpt-4" # GPT-4 export AZURE_GENERATIVE_MODEL_NAME="gpt-4-turbo" # GPT-4 Turbo # Use any embedding model (examples - check your Azure resource for availability) export AZURE_EMBEDDING_MODEL_NAME="text-embedding-ada-002" # Ada embeddings export AZURE_EMBEDDING_MODEL_NAME="text-embedding-3-large" # Large embeddings ``` **Important**: - Model availability depends on what you've deployed in your Azure OpenAI resource - Not all models are available in all Azure regions - Check your Azure OpenAI resource deployment to see which models are available ## Authentication Priority The service follows this authentication priority: 1. **API Key** (highest priority - for backward compatibility) 2. **Azure AD** (fallback when no API key is present) ## Required Azure Permissions For Azure AD authentication, ensure your identity has the following role on the Azure OpenAI resource: - **Cognitive Services OpenAI User**: Required for accessing Azure OpenAI services ## Usage Example ```python import parlant.sdk as p from parlant.sdk import NLPServices async with p.Server(nlp_service=NLPServices.azure) as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) ``` ## Server Deployment Guide ### Setting Up Service Principal for Production 1. **Create Service Principal**: ```bash # Login as admin user az login # Create service principal az ad sp create-for-rbac --name "parlant-service-principal" --role "Cognitive Services OpenAI User" --scopes "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP/providers/Microsoft.CognitiveServices/accounts/YOUR_OPENAI_RESOURCE" ``` 2. **Configure Environment Variables**: ```bash export AZURE_ENDPOINT="https://your-resource.openai.azure.com/" export AZURE_CLIENT_ID="appId-from-step-1" export AZURE_CLIENT_SECRET="password-from-step-1" export AZURE_TENANT_ID="tenant-from-step-1" ``` 3. **Test Authentication**: ```bash # Verify the service principal can access Azure OpenAI python -c " from parlant.adapters.nlp.azure_service import AzureService error = AzureService.verify_environment() print('Configuration OK' if error is None else f'Error: {error}') " ``` ### Configuration Tips ### Development Setup ```bash export AZURE_ENDPOINT="https://my-resource.openai.azure.com/" export AZURE_API_KEY="your-api-key" export AZURE_GENERATIVE_MODEL_NAME="gpt-4o-mini" ``` ### Production Setup (Azure AD) ```bash export AZURE_ENDPOINT="https://my-resource.openai.azure.com/" export AZURE_CLIENT_ID="your-client-id" export AZURE_CLIENT_SECRET="your-client-secret" export AZURE_TENANT_ID="your-tenant-id" export AZURE_GENERATIVE_MODEL_NAME="gpt-4o" ``` ## Troubleshooting ### Common Issues 1. **Authentication Failures** ``` Azure authentication is not properly configured. ``` **Solution**: - For development: Run `az login` (only for local development) - For production: Use service principal variables (NOT `az login`) - Ensure "Cognitive Services OpenAI User" role is assigned - Verify service principal has correct permissions 2. **Rate Limit Errors** ``` Azure API rate limit exceeded ``` **Solution**: - Check Azure account balance and billing status - Review API usage limits in Azure dashboard - Consider upgrading service tier 3. **Model Access Denied** ``` Model not found or access denied ``` **Solution**: - Verify model is deployed in your Azure OpenAI resource - Check regional availability - Ensure proper permissions 4. **Connection Errors** ``` Cannot connect to Azure OpenAI endpoint ``` **Solution**: - Verify `AZURE_ENDPOINT` is correct - Check network connectivity - Ensure firewall allows Azure OpenAI traffic ## Available Model Classes The service provides these pre-configured model classes for convenience, but supports any Azure OpenAI model: ### Pre-configured Classes - `GPT_4o`: Most capable GPT-4 model (128K context) - **Default** - `GPT_4o_Mini`: Faster, cost-effective GPT-4 (128K context) - `AzureTextEmbedding3Large`: High-quality embeddings (3072 dimensions) - **Default** - `AzureTextEmbedding3Small`: Efficient embeddings (3072 dimensions) ### Custom Model Classes - `CustomAzureSchematicGenerator`: Uses any generative model via `AZURE_GENERATIVE_MODEL_NAME` - `CustomAzureEmbedder`: Uses any embedding model via `AZURE_EMBEDDING_MODEL_NAME` **The service automatically chooses the appropriate class based on your environment variables.** ### How Model Selection Works The service uses this logic to select the appropriate model class: ```python # Generative Model Selection if AZURE_GENERATIVE_MODEL_NAME is set: use CustomAzureSchematicGenerator # Any model you specify else: use GPT_4o # Default model # Embedding Model Selection if AZURE_EMBEDDING_MODEL_NAME is set: use CustomAzureEmbedder # Any embedding model you specify else: use AzureTextEmbedding3Large # Default embedding model ``` This means you can use **any Azure OpenAI model** without code changes - just set the environment variables! ### Example: Using Different Models ```bash # Use GPT-3.5 Turbo (if available in your region) export AZURE_ENDPOINT="https://your-resource.openai.azure.com/" export AZURE_GENERATIVE_MODEL_NAME="gpt-35-turbo" export AZURE_EMBEDDING_MODEL_NAME="text-embedding-ada-002" # Use GPT-4 Turbo (if available in your region) export AZURE_GENERATIVE_MODEL_NAME="gpt-4-turbo" export AZURE_EMBEDDING_MODEL_NAME="text-embedding-3-large" # Use default models (GPT-4o and text-embedding-3-large) export AZURE_ENDPOINT="https://your-resource.openai.azure.com/" # No need to set AZURE_GENERATIVE_MODEL_NAME or AZURE_EMBEDDING_MODEL_NAME ``` ## Security Notes - **API Keys**: Store securely, rotate regularly - **Azure AD**: Use managed identities in production - **Network**: Ensure proper network security groups - **Monitoring**: Monitor usage and access patterns - **Compliance**: Follow organizational security policies ## Migration Guide ### From API Key to Azure AD 1. Set up Azure AD authentication using one of the supported methods 2. Remove the API key from your environment variables 3. Verify permissions - ensure your identity has "Cognitive Services OpenAI User" role 4. Test the configuration using `AzureService.verify_environment()` ### Backward Compatibility The service maintains full backward compatibility: - Existing API key configurations continue to work - No changes required for existing deployments - Gradual migration to Azure AD is supported ================================================ FILE: docs/adapters/nlp/ollama.md ================================================ # Ollama Service Documentation The Ollama service provides local LLM capabilities for Parlant using [Ollama](https://ollama.ai/). This service supports both text generation and embeddings using various open-source models. ## Prerequisites 1. **Install Ollama**: Download and install from [ollama.ai](https://ollama.ai/) 2. **Start Ollama server**: Run `ollama serve` (usually starts automatically) 3. **Pull required models** (see [Recommended Models](#recommended-models) section) ## Environment Variables Configure the Ollama service using these environment variables: ```bash # Ollama server URL (default: http://localhost:11434) export OLLAMA_BASE_URL="http://localhost:11434" # Model size to use (default: 4b) # Options: gemma3:1b, gemma3:4b, llama3.1:8b, gemma3:12b, gemma3:27b, llama3.1:70b, llama3.1:405b export OLLAMA_MODEL="gemma3:4b" # Embedding model (default: nomic-embed-text) # Options: nomic-embed-text, mxbai-embed-large export OLLAMA_EMBEDDING_MODEL="nomic-embed-text" # API timeout in seconds (default: 300) export OLLAMA_API_TIMEOUT="300" ``` ### Example Configuration ```bash # For development (fast, good balance) export OLLAMA_MODEL="gemma3:4b" export OLLAMA_EMBEDDING_MODEL="nomic-embed-text" export OLLAMA_API_TIMEOUT="180" # higher accuracy cloud export OLLAMA_MODEL="gemma3:4b" export OLLAMA_EMBEDDING_MODEL="nomic-embed-text" export OLLAMA_API_TIMEOUT="600" ``` ## Recommended Models **⚠️ IMPORTANT**: Pull these models before running Parlant to avoid API timeouts during first use: ### Text Generation Models ```bash # Recommended for most use cases (good balance of speed/accuracy) ollama pull gemma3:4b-it-qat # Fast but may struggle with complex schemas ollama pull gemma3:1b # embedding model required for creating embeddings ollama pull nomic-embed-text ``` ### Large Models (Cloud/High-end Hardware Only) ```bash # Better reasoning capabilities ollama pull llama3.1:8b # High accuracy for complex tasks ollama pull gemma3:12b # Very high accuracy (requires more resources) ollama pull gemma3:27b-it-qat # ⚠️ WARNING: Requires 40GB+ GPU memory ollama pull llama3.1:70b # ⚠️ WARNING: Requires 200GB+ GPU memory (cloud-only) ollama pull llama3.1:405b ``` ### Embedding Models To use custom embedding model set OLLAMA_EMBEDDING_MODEL environment value as required name Note that this implementation is tested using nomic-embed-text **⚠️ IMPORTANT**: Support for using other embedding models has been added including a custom embedding model of your own choice Ensure to set OLLAMA_EMBEDDING_VECTOR_SIZE which is compatible with your own embedding model before starting the server Tested with `snowflake-arctic-embed` with vector size of 1024 It is not NECESSARY to put OLLAMA_EMBEDDING_VECTOR_SIZE if you are using the supported `nomic-embed-text`, `mxbai-embed-large` or `bge-m3`. The vector size defaults to 768, 1024 and 1024 respectively for these ```bash # Alternative embedding model (512 dimensions) ollama pull mxbai-embed-large:latest ``` ## Model Recommendations by Use Case | Model Size | Use Case | Memory Requirements | Performance | |------------|----------|-------------------|-------------| | `1b` | Quick testing, simple tasks | ~2GB | Fast but limited accuracy | | `4b` | **Recommended for development** | ~4GB | Good balance of speed/accuracy | | `8b` | complex reasoning | ~8GB | Better reasoning than Gemma | | `12b` | High-accuracy tasks | ~12GB | High accuracy, slower | | `27b` | Complex workloads | ~27GB | Very high accuracy | | `70b` | Enterprise/cloud only | ~40GB+ | Excellent accuracy | | `405b` | Research/cloud only | ~200GB+ | State-of-the-art | ## Usage Example ```python import parlant.sdk as p from parlant.sdk import NLPServices async with p.Server(nlp_service=NLPServices.ollama) as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) ``` ## Configuration Tips ### Development Setup ```bash export OLLAMA_MODEL=gemma3:4b export OLLAMA_API_TIMEOUT=180 ``` ### High-Performance Setup (Cloud) ```bash export OLLAMA_MODEL=llama3.1:70b export OLLAMA_API_TIMEOUT=300 ``` ### Custom / Other models ```bash export OLLAMA_MODEL=llama3.2:3b export OLLAMA_API_TIMEOUT=300 ``` ## Troubleshooting ### Common Issues 1. **Model Not Found Error** ``` Model gemma3:4b not found. Please pull it first with: ollama pull gemma3:4b ``` **Solution**: Run `ollama pull gemma3:4b-it-qat` before starting Parlant 2. **Connection Error** ``` Cannot connect to Ollama server at http://localhost:11434 ``` **Solution**: Ensure Ollama is running with `ollama serve` 3. **Timeout Error** ``` Request timed out after 300s ``` **Solution**: Increase `OLLAMA_API_TIMEOUT` or use a smaller model 4. **Out of Memory** ``` CUDA out of memory ``` **Solution**: Use a smaller model size or increase GPU memory ### Performance Optimization 1. **Pre-pull models**: Always pull models before first use 2. **Adjust timeout**: Increase timeout for larger models 3. **Model selection**: Use smallest model that meets accuracy requirements 4. **GPU memory**: Monitor GPU usage and adjust model size accordingly ## Available Model Classes The service provides these pre-configured model classes: - `OllamaGemma3_1B`: Fast, basic accuracy - `OllamaGemma3_4B`: **Recommended** - good balance - `OllamaLlama31_8B`: Better reasoning - `OllamaGemma3_12B`: High accuracy - `OllamaGemma3_27B`: Very high accuracy - `OllamaLlama31_70B`: Enterprise-grade (high memory) - `OllamaLlama31_405B`: Research-grade (very high memory) ## Security Notes - Ollama runs locally, so no data leaves your machine - No API keys required - Models are downloaded and cached locally - Consider firewall rules if exposing Ollama server externally ================================================ FILE: docs/adapters/nlp/openrouter.md ================================================ # OpenRouter Service Documentation The OpenRouter service provides access to **400+ AI models** through a single unified API, including GPT-4, Claude, Llama, Qwen, and many more. OpenRouter makes it easy to switch between different models for both text generation and embeddings without changing code. ## Prerequisites 1. **OpenRouter Account**: Sign up at [openrouter.ai](https://openrouter.ai) 2. **API Key**: Get your API key from the [OpenRouter dashboard](https://openrouter.ai/keys) 3. **Model Access**: Ensure you have access to the models you want to use ## Quick Start ### Basic Setup ```bash # Set your OpenRouter API key (required) export OPENROUTER_API_KEY="your-api-key-here" # Optionally set default models export OPENROUTER_MODEL="openai/gpt-4o-mini" export OPENROUTER_EMBEDDER_MODEL="openai/text-embedding-3-large" ``` ### Minimal Example ```python import parlant.sdk as p from parlant.sdk import NLPServices async with p.Server(nlp_service=NLPServices.openrouter) as server: agent = await server.create_agent( name="AI Assistant", description="A helpful assistant powered by OpenRouter.", ) # 🎉 Ready to use at http://localhost:8800 ``` ## Configuration All configuration is done via environment variables. Set the required and optional environment variables before running your application: ```bash # Required: API Key export OPENROUTER_API_KEY="your-api-key-here" # Optional: LLM Configuration export OPENROUTER_MODEL="openai/gpt-4o-mini" export OPENROUTER_MAX_TOKENS="128000" # Optional: Embedding Configuration export OPENROUTER_EMBEDDER_MODEL="qwen/qwen3-embedding-8b" export OPENROUTER_EMBEDDER_DIMENSIONS="4096" # Optional override # Optional: Analytics export OPENROUTER_HTTP_REFERER="https://myapp.com" export OPENROUTER_SITE_NAME="My Application" ``` ## Environment Variables Reference ### Required Variables | Variable | Description | Example | |----------|-------------|---------| | `OPENROUTER_API_KEY` | Your OpenRouter API key | `sk-or-v1-...` | ### Optional Variables - LLM Configuration | Variable | Description | Default | Example | |----------|-------------|---------|---------| | `OPENROUTER_MODEL` | LLM model name | `openai/gpt-4o` | `openai/gpt-4o-mini` | | `OPENROUTER_MAX_TOKENS` | Max tokens limit | Auto-detected | `128000` | ### Optional Variables - Embedding Configuration | Variable | Description | Default | Example | |----------|-------------|---------|---------| | `OPENROUTER_EMBEDDER_MODEL` | Embedding model name | `openai/text-embedding-3-large` | `qwen/qwen3-embedding-8b` | | `OPENROUTER_EMBEDDER_DIMENSIONS` | Override embedding dimensions | Auto-detected | `4096` | ### Optional Variables - Analytics | Variable | Description | Example | |----------|-------------|---------| | `OPENROUTER_HTTP_REFERER` | Your app's URL (for analytics) | `https://myapp.com` | | `OPENROUTER_SITE_NAME` | Your app's name (for analytics) | `My Application` | ## Supported Models OpenRouter supports **400+ models** from different providers. Models are automatically optimized with specialized configurations when available. ### Pre-configured LLM Models These models have specialized configurations for optimal performance: | Model | Provider | Context | Use Case | |-------|----------|---------|----------| | `openai/gpt-4o` | OpenAI | 128K | Default, best overall quality | | `openai/gpt-4o-mini` | OpenAI | 128K | Cost-effective, fast | | `anthropic/claude-3.5-sonnet` | Anthropic | 200K | Advanced reasoning, long context | | `meta-llama/llama-3.3-70b-instruct` | Meta | 8K | Open-source option | ### Supported Embedding Models The service supports multiple embedding models with automatic dimension detection: | Model | Dimensions | Provider | Use Case | |-------|------------|----------|----------| | `openai/text-embedding-3-large` | 3072 | OpenAI | Default, high quality | | `openai/text-embedding-3-small` | 1536 | OpenAI | Faster, smaller | | `openai/text-embedding-ada-002` | 1536 | OpenAI | Legacy model | | `qwen/qwen3-embedding-8b` | 4096 | Qwen | High dimension, multilingual | | `qwen/qwen-embedding-v2` | 1536 | Qwen | Multilingual embeddings | ### Using Any OpenRouter Model You can use **any model** that OpenRouter supports by setting the appropriate environment variables: ```bash # LLM Models export OPENROUTER_MODEL="google/gemini-pro-1.5" # Embedding Models export OPENROUTER_EMBEDDER_MODEL="qwen/qwen3-embedding-8b" ``` Check the [OpenRouter Models page](https://openrouter.ai/models) for the full list of available models. ## Usage Examples ### Example 1: Default Configuration Use the default models (GPT-4o for LLM, text-embedding-3-large for embeddings): ```python import parlant.sdk as p from parlant.sdk import NLPServices async with p.Server(nlp_service=NLPServices.openrouter) as server: agent = await server.create_agent( name="General Assistant", description="A helpful AI assistant." ) ``` ### Example 2: Custom LLM Model Use Claude for text generation: ```bash export OPENROUTER_MODEL="anthropic/claude-3.5-sonnet" ``` ```python async with p.Server(nlp_service=NLPServices.openrouter) as server: agent = await server.create_agent( name="Claude Assistant", description="Powered by Claude." ) ``` ### Example 3: Custom Embedder Model Use a custom embedding model for better multilingual support: ```bash export OPENROUTER_MODEL="openai/gpt-4o-mini" export OPENROUTER_EMBEDDER_MODEL="qwen/qwen3-embedding-8b" ``` ```python async with p.Server(nlp_service=NLPServices.openrouter) as server: agent = await server.create_agent( name="Multilingual Assistant", description="Supports multiple languages." ) ``` ### Example 4: High-Performance Setup Optimize for speed and quality: ```bash export OPENROUTER_MODEL="openai/gpt-4o-mini" export OPENROUTER_EMBEDDER_MODEL="openai/text-embedding-3-large" export OPENROUTER_MAX_TOKENS="128000" ``` ```python async with p.Server(nlp_service=NLPServices.openrouter) as server: agent = await server.create_agent( name="High-Performance Agent", description="Optimized for speed and accuracy." ) ``` ### Example 5: Cost-Optimized Setup Balance quality and cost: ```bash export OPENROUTER_MODEL="openai/gpt-4o-mini" export OPENROUTER_EMBEDDER_MODEL="openai/text-embedding-3-small" ``` ```python async with p.Server(nlp_service=NLPServices.openrouter) as server: agent = await server.create_agent( name="Cost-Optimized Agent", description="Optimized for cost-effectiveness." ) ``` ## Embedding Model Configuration ### Understanding Embedding Dimensions Different embedding models produce vectors of different dimensions. The service automatically detects dimensions for known models, and can auto-detect from API responses for unknown models. ### Known Embedding Dimensions The following models have pre-configured dimensions: - `openai/text-embedding-3-large`: **3072** dimensions - `openai/text-embedding-3-small`: **1536** dimensions - `openai/text-embedding-ada-002`: **1536** dimensions - `qwen/qwen3-embedding-8b`: **4096** dimensions - `qwen/qwen-embedding-v2`: **1536** dimensions ### Auto-Detection For unknown models, dimensions are automatically detected from the first API response and cached for subsequent use. ### Manual Dimension Override If needed, you can manually specify dimensions via environment variable: ```bash export OPENROUTER_EMBEDDER_DIMENSIONS="4096" ``` ⚠️ **Important**: If you change embedder models or dimensions, you may need to clear your vector database cache to avoid dimension mismatch errors. ## Dynamic Model Selection OpenRouter intelligently handles model selection and configuration: ### Automatic Generator Selection Known models use specialized generators for optimal performance: - `openai/gpt-4o` → `OpenRouterGPT4O` - `openai/gpt-4o-mini` → `OpenRouterGPT4OMini` - `anthropic/claude-3.5-sonnet` → `OpenRouterClaude35Sonnet` - `meta-llama/llama-3.3-70b-instruct` → `OpenRouterLlama33_70B` - Other models → Dynamic generator with auto-configured parameters ### Automatic Embedder Selection Embedders are automatically configured based on the model name: - Known models → Pre-configured dimensions - Unknown models → Auto-detected dimensions from API response - Dynamic embedder → Created with proper container resolution ## Advantages of OpenRouter 1. **Model Diversity**: Access to 400+ models from different providers 2. **Unified Embeddings**: Native support for embedding models via the same API 3. **Cost Flexibility**: Choose models based on price-performance needs 4. **Single API**: One integration for multiple providers 5. **Auto-Optimization**: Automatic configuration for known models 6. **Environment-Based Configuration**: All configuration via environment variables 7. **Analytics**: Built-in usage tracking through OpenRouter dashboard ## Troubleshooting ### Rate Limit Errors **Error:** ``` OpenRouter API rate limit exceeded ``` **Solutions:** - Check your OpenRouter account balance and billing status - Review usage limits in the [OpenRouter dashboard](https://openrouter.ai/keys) - Consider upgrading your plan for higher limits - Try a different model with higher rate limits - Wait a moment before retrying ### JSON Mode Not Supported **Error:** ``` Model 'xyz' does not support JSON mode ``` **Solutions:** - OpenRouter automatically falls back to prompting for JSON output - Consider using a model that supports JSON mode: - `openai/gpt-4o` - `openai/gpt-4o-mini` - `anthropic/claude-3.5-sonnet` - The fallback still produces structured output reliably ### Dimension Mismatch Errors **Error:** ``` ValueError: all the input array dimensions except for the concatenation axis must match exactly ``` **Solutions:** - This occurs when switching embedder models with different dimensions - Clear your vector database cache/embeddings - Or delete the cached embeddings files in your `parlant-data` directory - The embedder will create new embeddings with the correct dimensions ### Authentication Errors **Error:** ``` OPENROUTER_API_KEY is not set ``` **Solutions:** - Set the `OPENROUTER_API_KEY` environment variable - Verify your API key in the [OpenRouter dashboard](https://openrouter.ai/keys) - Ensure the key hasn't expired or been revoked - Check for typos in the environment variable name ### Container Resolution Errors **Error:** ``` Unable to construct dependency of type OpenRouterEmbedder ``` **Solutions:** - This is automatically handled by the dynamic embedder class - Ensure you're using the latest version of the code - If the error persists, check that `embedder_model_name` is correctly set ## Cost Management OpenRouter provides transparent pricing across models. Choose models based on your needs: ### Cost-Effective LLM Options ```python # GPT-4o-mini - Good quality, lower cost model_name="openai/gpt-4o-mini" # Claude Haiku - Fast, affordable model_name="anthropic/claude-3-haiku" # Llama - Open source, very affordable model_name="meta-llama/llama-3.3-70b-instruct" ``` ### Cost-Effective Embedding Options ```python # text-embedding-3-small - Smaller, faster, cheaper embedder_model_name="openai/text-embedding-3-small" # text-embedding-ada-002 - Legacy, very affordable embedder_model_name="openai/text-embedding-ada-002" ``` ### Premium Options ```python # GPT-4o - Highest quality model_name="openai/gpt-4o" # text-embedding-3-large - Highest quality embeddings embedder_model_name="openai/text-embedding-3-large" ``` Check [OpenRouter pricing](https://openrouter.ai/docs/pricing) for current rates. ## Model Selection Guide ### When to Use Each LLM Model **GPT-4o** (`openai/gpt-4o`) - Complex reasoning tasks - Code generation and debugging - Multi-step problem solving - When accuracy is critical - Best overall performance **GPT-4o-mini** (`openai/gpt-4o-mini`) - General purpose tasks - High-volume applications - Cost-sensitive use cases - When 95% accuracy is sufficient - Fast response times **Claude** (`anthropic/claude-3.5-sonnet`) - Long context tasks (200K tokens) - Creative writing - Detailed analysis - When you need extended reasoning - Complex document understanding **Llama** (`meta-llama/llama-3.3-70b-instruct`) - Open-source requirements - Custom fine-tuning needs - Privacy-sensitive applications - Cost optimization - Self-hosted deployments ### When to Use Each Embedding Model **text-embedding-3-large** (`openai/text-embedding-3-large`) - Default choice for most use cases - High quality semantic search - Best accuracy for retrieval - Recommended for production **text-embedding-3-small** (`openai/text-embedding-3-small`) - Cost-sensitive applications - Faster embedding generation - Good quality for most tasks - Large-scale deployments **qwen3-embedding-8b** (`qwen/qwen3-embedding-8b`) - Multilingual applications - Higher dimensional space (4096) - Better fine-grained distinctions - When you need more embedding dimensions ## Best Practices ### 1. Start with Defaults Begin with the default models (`gpt-4o` and `text-embedding-3-large`) for best balance of quality and performance. ### 2. Use Mini for Scale Switch to `gpt-4o-mini` for high-volume operations where cost is a concern. ### 3. Match Embedder to Use Case - Use `text-embedding-3-large` for quality-critical applications - Use `text-embedding-3-small` for cost-sensitive deployments - Use `qwen3-embedding-8b` for multilingual or high-dimensional needs ### 4. Set Max Tokens Prevent runaway costs by setting appropriate `max_tokens` limits via environment variable: ```bash export OPENROUTER_MAX_TOKENS="128000" # For long-context models export OPENROUTER_MAX_TOKENS="8192" # For standard use cases ``` ### 5. Monitor Costs Regularly check the [OpenRouter dashboard](https://openrouter.ai/keys) to monitor usage and costs. ### 6. Use Analytics Set `OPENROUTER_HTTP_REFERER` and `OPENROUTER_SITE_NAME` to track usage across different applications. ### 7. Clear Cache When Changing Models If you switch embedder models, clear your vector database cache to avoid dimension mismatches. ### 8. Environment Variables for Production Use environment variables for production deployments instead of hardcoding values: ```bash # Production configuration export OPENROUTER_API_KEY="sk-or-v1-..." export OPENROUTER_MODEL="openai/gpt-4o-mini" export OPENROUTER_EMBEDDER_MODEL="openai/text-embedding-3-large" ``` ## Advanced Configuration ### Custom Dimensions for Unknown Models If using an embedding model not in the known list, you can specify dimensions: ```bash export OPENROUTER_EMBEDDER_MODEL="custom/embedding-model" export OPENROUTER_EMBEDDER_DIMENSIONS="2048" ``` The service will also auto-detect dimensions from the first API response. ### Combining Multiple Configurations All configuration is done via environment variables. Set multiple variables to configure different aspects: ```bash # Set all configuration via environment variables export OPENROUTER_MODEL="anthropic/claude-3.5-sonnet" export OPENROUTER_MAX_TOKENS="200000" export OPENROUTER_EMBEDDER_MODEL="openai/text-embedding-3-large" ``` ## Additional Resources - [OpenRouter Documentation](https://openrouter.ai/docs) - [Available Models](https://openrouter.ai/models) - [API Reference](https://openrouter.ai/docs/api-reference) - [Pricing Information](https://openrouter.ai/docs/pricing) - [Rate Limits](https://openrouter.ai/docs/api-reference/limits) ## Example: Complete Setup Here's a complete example showing a production-ready setup: ```bash # Set environment variables export OPENROUTER_API_KEY="your-api-key-here" export OPENROUTER_MODEL="openai/gpt-4o-mini" export OPENROUTER_EMBEDDER_MODEL="openai/text-embedding-3-large" export OPENROUTER_MAX_TOKENS="32768" ``` ```python import parlant.sdk as p from parlant.sdk import NLPServices @p.tool async def get_weather(context: p.ToolContext, city: str) -> p.ToolResult: # Your weather API logic here return p.ToolResult(f"Sunny, 72°F in {city}") async def main(): async with p.Server(nlp_service=NLPServices.openrouter) as server: agent = await server.create_agent( name="Weather Assistant", description="Helps users check weather conditions." ) await agent.create_guideline( condition="User asks about weather", action="Get weather information using the get_weather tool", tools=[get_weather] ) # 🎉 Ready at http://localhost:8800 if __name__ == "__main__": import asyncio asyncio.run(main()) ``` This setup provides: - ✅ Cost-effective LLM (`gpt-4o-mini`) - ✅ High-quality embeddings (`text-embedding-3-large`) - ✅ Reasonable token limit (32K) - ✅ Tool integration - ✅ Guideline-based behavior control ================================================ FILE: docs/adapters/nlp/snowflake-cortex.md ================================================ # Snowflake Cortex Adapter Integrate [Snowflake Cortex REST APIs](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-rest-api) for **chat/structured generation** and **embeddings** with Snowflake-hosted LLMs. The adapter talks directly to: - `POST /api/v2/cortex/inference:complete` for chat/JSON output - `POST /api/v2/cortex/inference:embed` for embeddings ## Requirements ### Authentication See [Snowflake REST API authentication](https://docs.snowflake.com/en/developer-guide/snowflake-rest-api/authentication). PAT is recommended. ### Environment Variables See [Cortex models](https://docs.snowflake.com/en/user-guide/snowflake-cortex/llm) for available model names. ```bash export SNOWFLAKE_CORTEX_BASE_URL="https://.snowflakecomputing.com" export SNOWFLAKE_AUTH_TOKEN="" export SNOWFLAKE_CORTEX_CHAT_MODEL="mistral-large2" export SNOWFLAKE_CORTEX_EMBED_MODEL="e5-base-v2" # Optional: export SNOWFLAKE_CORTEX_MAX_TOKENS="8192" ``` ## Usage Example ```python import parlant.sdk as p from parlant.sdk import NLPServices @p.tool async def get_weather(context: p.ToolContext, city: str) -> p.ToolResult: # Your weather API logic here return p.ToolResult(f"Sunny, 72°F in {city}") @p.tool async def get_datetime(context: p.ToolContext) -> p.ToolResult: from datetime import datetime return p.ToolResult(datetime.now()) async def main(): async with p.Server(nlp_service=NLPServices.snowflake) as server: agent = await server.create_agent( name="WeatherBot", description="Helpful weather assistant" ) # Have the agent's context be updated on every response (though # update interval is customizable) using a context variable. await agent.create_variable(name="current-datetime", tool=get_datetime) # Control and guide agent behavior with natural language await agent.create_guideline( condition="User asks about weather", action="Get current weather and provide a friendly response with suggestions", tools=[get_weather] ) # Add other (reliably enforced) behavioral modeling elements # ... # 🎉 Test playground ready at http://localhost:8800 # Integrate the official React widget into your app, # or follow the tutorial to build your own frontend! if __name__ == "__main__": import asyncio asyncio.run(main()) ``` ## Configuration Reference | Variable | Required | Description | |----------------------------------|----------|-------------| | `SNOWFLAKE_CORTEX_BASE_URL` | ✅ | Base account URL (e.g., `https://.snowflakecomputing.com`). | | `SNOWFLAKE_AUTH_TOKEN` | ✅ | OAuth / Keypair JWT / PAT used in the `Authorization: Bearer` header. | | `SNOWFLAKE_CORTEX_CHAT_MODEL` | ✅ | Chat model name. | | `SNOWFLAKE_CORTEX_EMBED_MODEL` | ✅ | Embedding model name. | | `SNOWFLAKE_CORTEX_MAX_TOKENS` | ❌ | Local upper bound for generation; does not override provider limits. | ## Notes on Privacy & Data Residency The adapter allows apps to call Cortex directly in your Snowflake account, reducing the need to move data outside Snowflake for LLM tasks. Review Snowflake's REST guidance for regional availability and account setup. ================================================ FILE: docs/adapters/nlp/vertex.md ================================================ # Vertex AI Service Adapter Documentation ## Overview The Vertex AI Service Adapter provides integration with Google Cloud's Vertex AI platform, supporting both Anthropic Claude models and Google Gemini models through their respective APIs. This adapter implements the Parlant NLP service interface for text generation, embeddings, and tokenization. ## Architecture ### Core Components - **VertexAIService**: Main service class implementing the NLPService interface - **VertexAIClaudeSchematicGenerator**: Generator for Claude models via Anthropic Vertex API - **VertexAIGeminiSchematicGenerator**: Generator for Gemini models via Google Gen AI API - **VertexAIEmbedder**: Text embedding service using Google's text-embedding-004 model - **VertexAIEstimatingTokenizer**: Token counting for both Claude and Gemini models ## Configuration ### Environment Variables ```bash # Required VERTEX_AI_PROJECT_ID=your-gcp-project-id VERTEX_AI_REGION=us-central1 # Put your region VERTEX_AI_MODEL=claude-opus-4 ``` ### Authentication The adapter uses Google Application Default Credentials (ADC): ```bash # For local development gcloud auth application-default login # For production, use service account key or workload identity ``` ## Supported Models ### Claude Models (via Anthropic Vertex API) | Short Name | Full Model Name | Description | |------------|-----------------|-------------| | `claude-opus-4` | `claude-opus-4@20250514` | Most capable Claude model | | `claude-sonnet-4` | `claude-sonnet-4@20250514` | Balanced performance and speed | | `claude-sonnet-3.5` | `claude-3-5-sonnet-v2@20241022` | Previous generation Sonnet | | `claude-haiku-3.5` | `claude-3-5-haiku@20241022` | Fastest Claude model | ### Gemini Models (via Google Gen AI API) | Short Name | Full Model Name | Description | |------------|-----------------|-------------| | `gemini-2.5-flash` | `gemini-2.5-flash` | Latest fast Gemini model | | `gemini-2.5-pro` | `gemini-2.5-pro` | Latest pro Gemini model | | `gemini-2.0-flash` | `gemini-2.0-flash` | Previous generation flash | | `gemini-1.5-flash` | `gemini-1.5-flash` | 1M token context | | `gemini-1.5-pro` | `gemini-1.5-pro` | 2M token context | ## Usage ### Basic Setup ```python import parlant.sdk import p from parlant.sdk import NLPServices async with p.Server(nlp_service=NLPServices.vertex) as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) ``` ### Direct Service Usage ```python from parlant.adapters.nlp.vertex_service import VertexAIService from parlant.core.loggers import Logger # Initialize service logger = Logger() service = VertexAIService(logger=logger) # Get schematic generator generator = await service.get_schematic_generator(YourSchemaClass) # Generate content result = await generator.generate( prompt="Your prompt here", hints={"temperature": 0.7, "max_tokens": 1000} ) ``` ## API Reference ### VertexAIService Main service class implementing the NLPService interface. #### Constructor ```python def __init__(self, logger: Logger) -> None ``` Initializes the service with environment variables: - Reads `VERTEX_AI_PROJECT_ID`, `VERTEX_AI_REGION`, `VERTEX_AI_MODEL` - Validates Application Default Credentials - Sets up logging #### Methods ##### get_schematic_generator ```python async def get_schematic_generator(self, t: type[T]) -> SchematicGenerator[T] ``` Returns appropriate generator based on configured model: - Claude models → VertexAIClaudeSchematicGenerator - Gemini models → VertexAIGeminiSchematicGenerator - Includes fallback logic for Claude Opus 4 ##### get_embedder ```python async def get_embedder(self) -> Embedder ``` Returns VertexTextEmbedding004 embedder instance. ##### get_moderation_service ```python async def get_moderation_service(self) -> ModerationService ``` Returns NoModeration service (moderation not yet implemented). ### VertexAIClaudeSchematicGenerator Schematic generator for Claude models via Anthropic Vertex API. #### Supported Hints - `temperature`: Controls randomness (0.0-1.0) - `max_tokens`: Maximum output tokens - `top_p`: Nucleus sampling parameter - `top_k`: Top-k sampling parameter #### Properties - `id`: Returns `vertex-ai/{model_name}` - `tokenizer`: Returns VertexAIEstimatingTokenizer instance - `max_tokens`: Returns 200,000 (Claude context limit) #### Methods ##### generate ```python async def generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T] ``` Generates structured content using Claude models with: - JSON schema validation - Retry policies for rate limits and errors - Usage tracking ### VertexAIGeminiSchematicGenerator Schematic generator for Gemini models via Google Gen AI API. #### Supported Hints - `temperature`: Controls randomness (0.0-1.0) - `thinking_config`: Configuration for reasoning models #### Properties - `id`: Returns `vertex-ai/{model_name}` - `tokenizer`: Returns VertexAIEstimatingTokenizer instance - `max_tokens`: Returns 1M (Flash) or 2M (Pro) tokens #### Methods ##### generate ```python async def generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T] ``` Generates structured content using Gemini models with: - Native JSON schema support - JSON parsing and validation - Usage metadata tracking ### VertexAIEmbedder Text embedding service using Google's text-embedding-004 model. #### Properties - `id`: Returns `vertex-ai/text-embedding-004` - `dimensions`: Returns 768 (embedding dimensions) - `max_tokens`: Returns 8,192 (input token limit) #### Supported Hints - `title`: Document title for better embeddings - `task_type`: Embedding task type (default: "RETRIEVAL_DOCUMENT") #### Methods ##### embed ```python async def embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult ``` Generates embeddings for input texts with batch processing support. ### VertexAIEstimatingTokenizer Token counting service supporting both Claude and Gemini models. #### Methods ##### estimate_token_count ```python async def estimate_token_count(self, prompt: str) -> int ``` Estimates token count using: - tiktoken for Claude models - Google Gen AI API for Gemini models ## Error Handling ### Authentication Errors ```python class VertexAIAuthError(Exception): """Raised when there are authentication issues with Vertex AI.""" ``` Common causes and solutions: - Missing ADC: Run `gcloud auth application-default login` - Insufficient permissions: Ensure "Vertex AI User" role - Model not enabled: Check Vertex AI Model Garden ### Rate Limiting The adapter implements comprehensive retry policies: #### Claude Models - Retries: APIConnectionError, APITimeoutError, RateLimitError, APIResponseValidationError - Max attempts: 3 with exponential backoff (1s, 2s, 4s) - Server errors: 2 attempts with longer delays (1s, 5s) #### Gemini Models - Retries: NotFound, TooManyRequests, ResourceExhausted - Max attempts: 3 with exponential backoff (1s, 2s, 4s) - Server errors: 2 attempts with longer delays (1s, 5s) ### Error Messages The adapter provides detailed error messages for common issues: #### Rate Limit Exceeded ``` Vertex AI rate limit exceeded. Possible reasons: 1. Your GCP project may have insufficient quota. 2. The model may not be enabled in Vertex AI Model Garden. 3. You might have exceeded the requests-per-minute limit. Recommended actions: - Check your Vertex AI quotas in the GCP Console. - Ensure the model is enabled in Vertex AI Model Garden. - Review IAM permissions for the service account. - Visit: https://console.cloud.google.com/vertex-ai/model-garden ``` #### Permission Denied ``` Permission denied accessing Vertex AI. Ensure: 1. ADC is properly configured (run 'gcloud auth application-default login') 2. The service account has 'Vertex AI User' role 3. The {model_name} model is enabled in Vertex AI Model Garden ``` ## Performance Considerations ### Token Limits | Model Type | Context Limit | Recommended Usage | |------------|---------------|-------------------| | Claude Models | 200K tokens | Long documents, complex reasoning | | Gemini Flash | 1M tokens | Large context processing | | Gemini Pro | 2M tokens | Maximum context requirements | ## Best Practices ### Model Selection 1. **Claude Sonnet 3.5**: Best balance of performance and cost 2. **Claude Opus 4**: Maximum capability with fallback 3. **Gemini 2.5 Flash**: Fast processing with large context 4. **Gemini 2.5 Pro**: Complex reasoning tasks ### Configuration ```python export VERTEX_AI_PROJECT_ID=your-project-id export VERTEX_AI_REGION=us-central1 export VERTEX_AI_MODEL=claude-sonnet-3.5 ``` ### Error Handling ```python from parlant.adapters.nlp.vertex_service import VertexAIAuthError try: service = VertexAIService(logger=logger) generator = await service.get_schematic_generator(MySchema) result = await generator.generate(prompt) except VertexAIAuthError as e: logger.error(f"Authentication failed: {e}") # Handle auth setup except Exception as e: logger.error(f"Generation failed: {e}") # Handle other errors ``` ## Troubleshooting ### Common Issues 1. **Authentication Failures** - Verify ADC setup: `gcloud auth application-default print-access-token` - Check project permissions in GCP Console - Ensure service account has required roles 2. **Model Access Denied** - Enable models in Vertex AI Model Garden - Check regional availability - Verify billing account is active 3. **Rate Limiting** - Monitor quota usage in GCP Console - Implement application-level rate limiting - Consider upgrading service tier ### Debugging Check usage from the playground UI by inspecting on the generated message ## Migration Guide ### From Other Adapters When migrating from other NLP adapters: 1. **Update Environment Variables** ```bash # Remove old variables unset OPENAI_API_KEY ANTHROPIC_API_KEY # Set Vertex AI variables export VERTEX_AI_PROJECT_ID=your-project-id export VERTEX_AI_REGION=us-central1 export VERTEX_AI_MODEL=claude-opus-4 ``` 2. **Model Name Mapping** - `gpt-4` → `claude-opus-4` - `gpt-3.5-turbo` → `gemini-2.5-flash` - `claude-3-sonnet` → `claude-opus-4` ## Contributing ### Adding New Models 1. **Determine Provider**: Check if model uses Anthropic or Google API 2. **Create Model Class**: Inherit from appropriate base generator 3. **Update Service**: Add model mapping in VertexAIService 4. **Add Tests**: Include integration tests for new model 5. **Update Documentation**: Add model to supported models table ### Code Style - Follow existing patterns for error handling - Include comprehensive logging - Add type hints for all methods - Document public APIs with docstrings - Use retry policies for external API calls ## Prerequisites and Installation ### Installation To use the Vertex AI Service Adapter with Parlant, you need to install the appropriate optional dependencies: ```bash pip install "parlant[vertex]" ``` This installation includes support for both Claude and Gemini models through the Vertex AI platform. ### Important Model Deprecation Notice ⚠️ **Claude 3.5 Sonnet Models Deprecation**: Claude Sonnet 3.5 models (claude-3-5-sonnet-20240620 and claude-3-5-sonnet-20241022) will be retired on October 22, 2025. We recommend migrating to Claude Sonnet 4 (claude-sonnet-4-20250514) for improved performance and capabilities. ## Authentication Setup Before using the adapter, ensure you have proper authentication configured: ```bash # For local development gcloud auth application-default login # Verify authentication gcloud auth application-default print-access-token ``` ## Required Permissions Ensure your service account or user has the following IAM roles: - `Vertex AI User` - for accessing Vertex AI services - `AI Platform User` - for model access (legacy role, may be needed for some models) ## License Licensed under the Apache License, Version 2.0. See the source file header for full license text. ## Maintainer Agam Dubey - hello.world.agam@gmail.com ================================================ FILE: docs/adapters/persistence/snowflake.md ================================================ # Snowflake Persistence Adapter The Snowflake document adapter lets Parlant persist the long–lived parts of a deployment—sessions, customers, and context variables—inside your Snowflake account. That means you can run the server (for example inside Snowpark Container Services), stop it, and later resume the exact same conversation state. This page walks through the required environment variables and shows how to wire the stores into Snowflake when booting Parlant via the SDK. ## Requirements 1. Install the optional dependency (or otherwise provide `snowflake-connector-python`): ```bash pip install "parlant[snowflake]" ``` 2. Set the credentials that `SnowflakeDocumentDatabase` consumes: | Variable | Required | Description | |-----------------------------|:--------:|-------------------------------------------------------------------------------------| | `SNOWFLAKE_ACCOUNT` | ✅ | Account locator (e.g. `abc-xy123`). | | `SNOWFLAKE_USER` | ✅ | Username that Parlant should authenticate as. | | `SNOWFLAKE_PASSWORD` | ✅* | Password for password-based auth. Skip when using OAuth (see `SNOWFLAKE_TOKEN`). | | `SNOWFLAKE_TOKEN` | ✅* | OAuth access token. When set, the adapter automatically switches to OAuth. | | `SNOWFLAKE_WAREHOUSE` | ✅ | Warehouse to execute queries against. | | `SNOWFLAKE_DATABASE` | ✅ | Database that will host the Parlant tables. | | `SNOWFLAKE_SCHEMA` | ✅ | Schema inside the database. | | `SNOWFLAKE_ROLE` | ➖ | Optional role override. | > ✅* Provide **either** `SNOWFLAKE_PASSWORD` **or** `SNOWFLAKE_TOKEN`. ## SDK / Module Setup Parlant’s SDK exposes a `configure_container` hook that lets you replace the default persistence layer. The pattern below shows how to register Snowflake-backed implementations of the three configurable stores: - `SessionStore` → `SessionDocumentStore` - `CustomerStore` → `CustomerDocumentStore` - `ContextVariableStore` → `ContextVariableDocumentStore` Each store receives its own table prefix (`PARLANT_SESSIONS_`, `PARLANT_CUSTOMERS_`, `PARLANT_CONTEXT_VARIABLES_`) so their metadata never collides. We also rebind `EventEmitterFactory`, so system events get written into the same store. ```python from contextlib import AsyncExitStack import parlant.sdk as p from parlant.adapters.db.snowflake_db import SnowflakeDocumentDatabase from parlant.core.emission.event_publisher import EventPublisherFactory EXIT_STACK = AsyncExitStack() async def _make_session_store(container: p.Container) -> p.SessionStore: database = await EXIT_STACK.enter_async_context( SnowflakeDocumentDatabase( logger=container[p.Logger], table_prefix="PARLANT_SESSIONS_", ) ) store = p.SessionDocumentStore(database=database, allow_migration=True) return await EXIT_STACK.enter_async_context(store) async def _make_customer_store(container: p.Container) -> p.CustomerStore: database = await EXIT_STACK.enter_async_context( SnowflakeDocumentDatabase( logger=container[p.Logger], table_prefix="PARLANT_CUSTOMERS_", ) ) store = p.CustomerDocumentStore( id_generator=container[p.IdGenerator], database=database, allow_migration=True, ) return await EXIT_STACK.enter_async_context(store) async def _make_variable_store(container: p.Container) -> p.ContextVariableStore: database = await EXIT_STACK.enter_async_context( SnowflakeDocumentDatabase( logger=container[p.Logger], table_prefix="PARLANT_CONTEXT_VARIABLES_", ) ) store = p.ContextVariableDocumentStore( id_generator=container[p.IdGenerator], database=database, allow_migration=True, ) return await EXIT_STACK.enter_async_context(store) async def configure_container(container: p.Container) -> p.Container: container = container.clone() session_store = await _make_session_store(container) container[p.SessionDocumentStore] = session_store container[p.SessionStore] = session_store customer_store = await _make_customer_store(container) container[p.CustomerDocumentStore] = customer_store container[p.CustomerStore] = customer_store variable_store = await _make_variable_store(container) container[p.ContextVariableDocumentStore] = variable_store container[p.ContextVariableStore] = variable_store container[p.EventEmitterFactory] = EventPublisherFactory( container[p.AgentStore], session_store, ) return container async def shutdown_snowflake() -> None: await EXIT_STACK.aclose() ``` ### Using the SDK ```python async def main() -> None: try: async with p.Server( nlp_service=p.NLPServices.snowflake, configure_container=configure_container, ) as server: ... finally: await shutdown_snowflake() ``` ## What Gets Persisted? Once the Snowflake stores are registered, Snowflake becomes the source of truth for: - Sessions + events + inspections - Customers + their tag associations - Context variables + their values Other stores (agents, guidelines, journeys, etc.) continue to use their default backends. If you define them in code at startup, they will automatically be recreated each time the server runs. For dynamic authoring flows you can follow the same module approach to route additional stores into Snowflake. ================================================ FILE: docs/adapters/vector_db/qdrant.md ================================================ # Qdrant Vector Database The Qdrant adapter provides persistent vector storage for Parlant using Qdrant's vector database. This replaces the default in-memory storage with production-ready persistence. For general Parlant usage, see the [official documentation](https://www.parlant.io/docs/). ## Prerequisites 1. **Install Qdrant adapter**: `pip install parlant[qdrant]` 2. **Choose storage**: Local file system or Qdrant Cloud ## Quick Start ### Setup (Manual) ```python import parlant.sdk as p from pathlib import Path from contextlib import AsyncExitStack from parlant.adapters.vector_db.qdrant import QdrantDatabase from parlant.core.nlp.embedding import EmbedderFactory, EmbeddingCache, Embedder from parlant.core.loggers import Logger from parlant.core.nlp.service import NLPService from parlant.core.glossary import GlossaryVectorStore, GlossaryStore from parlant.core.canned_responses import CannedResponseVectorStore, CannedResponseStore from parlant.core.capabilities import CapabilityVectorStore, CapabilityStore from parlant.core.journeys import JourneyVectorStore, JourneyStore from parlant.adapters.db.transient import TransientDocumentDatabase async def configure_container(container: p.Container) -> p.Container: embedder_factory = EmbedderFactory(container) async def get_embedder_type() -> type[Embedder]: return type(await container[NLPService].get_embedder()) exit_stack = AsyncExitStack() qdrant_db = await exit_stack.enter_async_context( QdrantDatabase( logger=container[Logger], path=Path("./qdrant_data"), embedder_factory=EmbedderFactory(container), embedding_cache_provider=lambda: container[EmbeddingCache], ) ) # For Qdrant Cloud, replace the above with: # qdrant_db = await exit_stack.enter_async_context( # QdrantDatabase( # logger=container[Logger], # url="https://your-cluster-id.us-east4-0.gcp.cloud.qdrant.io", # api_key="your-api-key-here", # embedder_factory=EmbedderFactory(container), # embedding_cache_provider=lambda: container[EmbeddingCache], # ) # ) # Configure stores using vector database container[GlossaryStore] = await exit_stack.enter_async_context( GlossaryVectorStore( id_generator=container[p.IdGenerator], vector_db=qdrant_db, document_db=TransientDocumentDatabase(), embedder_factory=embedder_factory, embedder_type_provider=get_embedder_type, ) # type: ignore ) container[CannedResponseStore] = await exit_stack.enter_async_context( CannedResponseVectorStore( id_generator=container[p.IdGenerator], vector_db=qdrant_db, document_db=TransientDocumentDatabase(), embedder_factory=embedder_factory, embedder_type_provider=get_embedder_type, ) # type: ignore ) container[CapabilityStore] = await exit_stack.enter_async_context( CapabilityVectorStore( id_generator=container[p.IdGenerator], vector_db=qdrant_db, document_db=TransientDocumentDatabase(), embedder_factory=embedder_factory, embedder_type_provider=get_embedder_type, ) # type: ignore ) container[JourneyStore] = await exit_stack.enter_async_context( JourneyVectorStore( id_generator=container[p.IdGenerator], vector_db=qdrant_db, document_db=TransientDocumentDatabase(), embedder_factory=embedder_factory, embedder_type_provider=get_embedder_type, ) # type: ignore ) return container async def main(): async with p.Server(configure_container=configure_container) as server: agent = await server.create_agent( name="My Agent", description="Agent using Qdrant for persistent storage", ) # Test: Create a term to verify Qdrant is working term = await agent.create_term( name="Example Term", description="This is stored in Qdrant", ) print(f"Created term: {term.name}") # All vector operations now use Qdrant ``` ## Verification To verify Qdrant integration is working correctly: ### Check Collections **Qdrant Cloud:** Collections appear in your Qdrant dashboard with names like: - `glossary_OpenAITextEmbedding3Large` - `glossary_unembedded` - `capabilities_OpenAITextEmbedding3Large` - `canned_responses_OpenAITextEmbedding3Large` **Local Qdrant:** A folder is created at your specified path containing Qdrant database files. ### Confirm No Transient Storage When Qdrant is properly configured: - **No vector files** are created in the `parlant-data` folder - Vector data is stored only in Qdrant (cloud or local) - Data persists across server restarts ### Test Vector Search Create terms and test persistence: ```python term = await agent.create_term( name="Test Term", description="This should be stored in Qdrant", ) # Then chat with agent about "test term" - it should understand via vector search # Test persistence: close the server and run again # The term should still be available after restart ``` --- ## Common Issues ### Integration Not Working (Still Using Transient Storage) **Symptoms:** - No collections appear in Qdrant dashboard - Vector data appears in `parlant-data` folder - Data lost on server restart **Solution:** Ensure all vector stores are properly configured with Qdrant in your `configure_container` function. Make sure you're using `AsyncExitStack` to properly manage the Qdrant database and vector stores lifecycle. ### Windows File Locks On Windows, use `async with` context manager. The adapter automatically handles file lock retries. ### Collection Sync Collections auto-sync when embedders or schemas change. Large collections may take time on first access. ### Embedder Changes When changing embedder types, old embedded collections persist until manually deleted. ### Performance Use Qdrant Cloud or server for production - local mode doesn't support payload indexes. You'll see a warning about this when using local Qdrant, which is expected and can be ignored. --- ## Troubleshooting ### Connection Issues - **Local**: Check path exists and is writable - **Remote**: Verify URL and API key ### Slow Performance - Use embedding cache - Use Qdrant Cloud/server for payload indexes - Consider splitting large collections ### Data Not Persisting - Check file path is correct and writable - Verify connection settings for remote servers - Test by closing the server and restarting—data should persist --- ## Requirements - Python 3.8+ - `pip install parlant[qdrant]` - Writable directory (for local storage) or Qdrant Cloud account ## Key Features - **Persistent storage**: Replaces in-memory storage with production-ready persistence - **Auto-sync**: Collections automatically sync when embedders or schemas change - **Windows support**: Automatic file lock handling ================================================ FILE: docs/advanced/contributing.md ================================================ # Contributing to Parlant We use the Linux-standard Developer Certificate of Origin ([DCO.md](https://github.com/emcie-co/parlant/blob/develop/DCO.md)), so that, by contributing, you confirm that you have the rights to submit your contribution under the Apache 2.0 license (i.e., the code you're contributing is truly yours to share with the project). Please consult [CONTRIBUTING.md](https://github.com/emcie-co/parlant/blob/develop/CONTRIBUTING.md) for more details. Want to start getting involved right now? Join us on [Discord](https://discord.gg/duxWqxKk6J) and let's discuss how you can help shape Parlant. We're excited to work with contributors directly while we set up our formal processes. Otherwise, feel free to start a discussion or open an issue on [GitHub](https://github.com/emcie-co/parlant). ================================================ FILE: docs/advanced/custom-llms.md ================================================ # Custom NLP Models Once you've understood the basic of setting up [engine extensions](https://parlant.io/docs/advanced/engine-extensions), you can integrate other NLP models into Parlant. > **A Note on Custom Models** > > Parlant was optimized to work with the built-in LLMs, so using other models may require additional configuration and testing. > > In particular, please note that Parlant uses some complex output JSON schemas in its operation. This means that you either need a powerful model that can handle complex outputs, or, alternatively, that you use a smaller model (SLM) that has been fine-tuned on Parlant data specifically, using a larger model as a teacher. > > Using smaller models is actually a great way to reduce costs, latency—and sometimes even accuracy—in production environments. ## Understanding `NLPService` Whether you want to use a different model from a supported built-in provider, or an entirely different provider, you can do so by creating a custom `NLPService` implementation. An `NLPService` has 3 key components: 1. **Schematic Generators**: These are used to generate structured content based on prompts. 2. **Embedders**: These are used to create vector representations of text for semantic retrieval. 3. **Moderation Service**: This is used to filter out harmful or inappropriate user input in conversations. > **Reference Example** > > You can take a look at the official [`OpenAIService`](https://github.com/emcie-co/parlant/blob/main/src/parlant/adapters/nlp/openai_service.py) for a production-ready reference implementation of an `NLPService`. ### Schematic Generation Throughout the Parlant engine, you'll find references to `SchematicGenerator[T]` objects. These are objects that generate [Pydantic](https://docs.pydantic.dev/latest/) models using instructions in a provided prompt. Behind the scenes, they always use LLMs to generate JSON schemas that in turn are converted to Pydantic models. All LLM requests in Parlant are actually made using these schematic generators, which means that, whatever model you use, it must be able to generate valid JSON schemas consistently. This is the only requirement for a model in Parlant. Let's now look at a few important interfaces that you need to implement in your custom NLP service. #### Estimating Tokenizers The `EstimatingTokenizer` interface is used to estimate the number of tokens in a prompt. This is important for managing costs and rate limits when using LLM APIs. It's also used in embedding models, where Parlant needs to chunk the input text into smaller parts to fit within the model's context window. The reason it's called "estimating" is because not all model APIs provide exact token counts. ```python class EstimatingTokenizer(ABC): """An interface for estimating the token count of a prompt.""" @abstractmethod async def estimate_token_count(self, prompt: str) -> int: """Estimate the number of tokens in the given prompt.""" ... ``` For example, with `OpenAI`, you can implement this to use the `tiktoken` library to get accurate token counts for GPT models, or estimated token counts for other popular models. #### Schematic Generators Now let's look at the `SchematicGenerator[T]` interface itself, which is used to generate structured content based on a prompt. Each generation result from a `SchematicGenerator[T]` contains not just the generated object, but also additional metadata about the generation process. Here's what it looks like: ```python @dataclass(frozen=True) class SchematicGenerationResult(Generic[T]): content: T # The generated schematic content (a Pydantic model instance) info: GenerationInfo # Metadata about the generation process @dataclass(frozen=True) class GenerationInfo: schema_name: str # The name of the Pydantic schema used for the generated content model: str # The name of the model used for generation duration: float # Time taken for the generation in seconds usage: UsageInfo # Token usage information @dataclass(frozen=True) class UsageInfo: input_tokens: int output_tokens: int extra: Optional[Mapping[str, int]] = None # May contain metrics like cached input tokens ``` Now let's look at the `SchematicGenerator[T]` interface itself, which you'd need to implement for your custom model: ```python class SchematicGenerator(ABC, Generic[T]): """An interface for generating structured content based on a prompt.""" @abstractmethod async def generate( self, # The prompt (or PromptBuilder) containing instructions for the generation. prompt: str | PromptBuilder, # Hints are a good way to provide additional context or parameters for the generation, # such as temperature, top P, logit bias, and things of that nature. hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: """Generate content based on the provided prompt and hints.""" # Implement this method to generate content using your own model. ... @property @abstractmethod def id(self) -> str: """Return a unique identifier for the generator.""" # Normally, this would be the model name or ID used in the LLM API. ... @property @abstractmethod def max_tokens(self) -> int: """Return the maximum number of tokens in the underlying model's context window.""" # Return the maximum number of tokens that can be processed by your model. ... @property @abstractmethod def tokenizer(self) -> EstimatingTokenizer: """Return a tokenizer that approximates that of the underlying model.""" # This tokenizer should be able to estimate token counts for prompts for this model. ... @cached_property def schema(self) -> type[T]: """Return the schema type for the generated content. This is useful for derived classes, allowing them to access the concrete schema type for the current instance without needing to know the type parameter. """ # You don't need to implement this method - it's an inherited convenience method. orig_class = getattr(self, "__orig_class__") generic_args = get_args(orig_class) return cast(type[T], generic_args[0]) ``` > **Reference Example** > > You can take a look at the official [`OpenAIService`](https://github.com/emcie-co/parlant/blob/main/src/parlant/adapters/nlp/openai_service.py) for a production-ready reference implementation of an `SchematicGenerator[T]`. ### Embedding In addition to generating structured content, Parlant also uses embedders to create vector representations of text. These are used for semantic retrieval where applicable throughout the response lifecycle. #### Embedding Results Every embedding operation returns an `EmbeddingResult`, which contains the vectors generated by the embedder: ```python @dataclass(frozen=True) class EmbeddingResult: vectors: Sequence[Sequence[float]] ``` #### Embedders Now let's look at the `Embedder` interface and how to implement it: ```python class Embedder(ABC): @abstractmethod async def embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: # Generate embeddings for the given texts. ... @property @abstractmethod def id(self) -> str: # Return a unique identifier for the embedder - usually the model name or ID. ... @property @abstractmethod def max_tokens(self) -> int: # Return the maximum number of tokens in the model's context window. ... @property @abstractmethod def tokenizer(self) -> EstimatingTokenizer: # Return a tokenizer that approximates the model's token count for prompts. ... @property @abstractmethod def dimensions(self) -> int: # Return the dimensionality of the embedding space. ... ``` > **Reference Example** > > You can take a look at the official [`OpenAIService`](https://github.com/emcie-co/parlant/blob/main/src/parlant/adapters/nlp/openai_service.py) for a production-ready reference implementation of an `Embedder`. ### Moderation Services Parlant includes a comprehensive content moderation system to filter harmful or inappropriate user input. The moderation service is the third key component of an `NLPService`, alongside schematic generators and embedders. #### Understanding Moderation in Parlant Parlant's moderation system provides content filtering capabilities that can detect and flag various types of harmful content before it reaches your AI agents. The engine can integrate with all stsandard moderation providers and can be configured with different levels of strictness. #### Moderation Interface All moderation services implement the `ModerationService` abstract base class: ```python @dataclass(frozen=True) class CustomerModerationContext: """Context for moderation check""" session: Session # Session context for the message being checked message: str # The content of the message to check @dataclass(frozen=True) class ModerationCheck: """Result of a moderation check.""" flagged: bool # Whether the content was flagged as inappropriate tags: list[str] # Specific categories that were flagged class ModerationService(ABC): """Abstract base class for content moderation services.""" @abstractmethod async def moderate_customer(self, context: CustomerModerationContext) -> ModerationCheck: """Check content for policy violations and return moderation result.""" ... ``` #### Moderation Tags Parlant uses standardized moderation tags that map to common content policy categories: ```python ModerationTag: TypeAlias = Literal[ "jailbreak", # Prompt injection attempts "harassment", # Harassment or bullying content "hate", # Hate speech or discrimination "illicit", # Illegal activities or substances "self-harm", # Self-harm or suicide content "sexual", # Sexual or adult content "violence", # Violence or graphic content ] ``` #### Implementing Custom Moderation Services Here's how to create your own moderation service: ```python import httpx import parlant.sdk as p class MyModerationService(p.ModerationService): def __init__(self, api_key: str, logger: p.Logger): self._api_key = api_key self._logger = logger self._client = httpx.AsyncClient() async def moderate_customer(self, context: p.CustomerModerationContext) -> p.ModerationCheck: """Implement your moderation logic here.""" try: # Example: Call your moderation API response = await self._client.post( "https://api.your-moderation-service.com/moderate", json={"text": context.message}, headers={"Authorization": f"Bearer {self._api_key}"} ) response.raise_for_status() result = response.json() # Map your service's response to Parlant's format flagged = result.get("flagged", False) categories = result.get("categories", []) # Convert your categories to Parlant's standardized tags tags = [] category_mapping = { "toxic": "harassment", "hate_speech": "hate", "violence": "violence", "sexual_content": "sexual", "self_harm": "self-harm", "illegal": "illicit", "prompt_injection": "jailbreak", } for category in categories: if category in category_mapping: tags.append(category_mapping[category]) return p.ModerationCheck( flagged=flagged, tags=tags, ) except Exception as e: self._logger.error(f"Moderation check failed: {e}") # Fail closed: return unflagged to allow content through # Or fail open: return flagged to block content return p.ModerationCheck(flagged=False, tags=[]) ``` ## Customizing Prompts When you implement your own `SchematicGenerator[T]`, you can also customize the prompts it actually uses. This is achieved via the `PromptBuilder` class. It's the same class used throughout the Parlant engine to build prompts for LLMs using consistent rules and formats, and it allows you to access and modify prompt templates. One of the cool things you can do with it is to edit specific prompt sections right before you build the final prompt. Let's look at an example of how we'd override the draft creation prompt of the `CannedResponseGenerator`. ```python class MySchematicGenerator(p.SchematicGenerator[p.T]): async def generate( self, prompt: str | p.PromptBuilder, hints: Mapping[str, Any] = {}, ) -> p.SchematicGenerationResult[T]: def edit_draft_instructions(section: p.PromptSection) -> p.PromptSection: # You can inspect the section's dynamically-passed properties # to see what you can make use of in your modified template. section.props section.template = f""" Write your custom instructions here ... Pass in dynamic props where needed: {section.props} """ return section prompt.edit_section( name="canned-response-generator-draft-general-instructions", editor_func=edit_draft_instructions, ) # Call the parent class's generate method with the modified prompt return await super().generate(prompt, hints) ``` You can modify any section used anywhere within Parlant. You can find these sections by looking at references to `PromptBuilder.add_section()` in the Parlant codebase. ## Implementing an `NLPService` Now that you understand the key interfaces, you can implement your own `NLPService`. This is the easy part. Here's what that would look like: ```python class MyNLPService(p.NLPService): def __init__(self, logger: p.Logger): self.logger = logger async def get_schematic_generator(self, t: type[p.T]) -> p.SchematicGenerator[p.T]: # Return your custom schematic generator for the given type. return MySchematicGenerator[p.T]( logger=self.logger, # Assuming you use a logger ) async def get_embedder(self) -> p.Embedder: return MyEmbedder( logger=self.logger, # Assuming you use a logger ) async def get_moderation_service(self) -> p.ModerationService: # Return your custom moderation service implementation. # If you don't need moderation, return NoModeration(). return MyModerationService(logger=self.logger) ``` ## Injecting a Custom `NLPService` Once you've implemented your custom `NLPService`, you can easily register it with your Parlant server. You also get a reference to the dependency-injection container, from which you can access the system's logger and other services, as needed. ```python def load_custom_nlp_service(container: p.Container) -> p.NLPService: return MyNLPService( logger=container[p.Logger] ) ``` Then, when you start your Parlant server, pass your loader function to the `nlp_service` parameter: ```python async with p.Server( nlp_service=load_custom_nlp_service, ) as server: # Your code here ``` ================================================ FILE: docs/advanced/engine-extensions.md ================================================ # Engine Extensions Working with an external framework and adapting it to your needs isn’t always simple, especially when you need it to behave in ways its original design didn’t anticipate. Modifying the framework’s source code is a treacherous path—not just because it requires deeper expertise, but also because it leads to divergences between your locally-modified version and upstream updates. So how do you get a pre-built framework to work differently? The idea is to be able to run a system or software that includes your code customizations without breaking its fundamental assumptions. The [Open/Closed Principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) states that software should be open for extension, but closed for modification, such that it can allow its behavior to be extended without modifying its source code. Parlant is carefully designed to abide by this principle, allowing you to achieve extreme extensibility by hooking into its structure. With extensions, you are free to build exactly what you need without waiting for updates or modifying core engine components. This is a good time to remind you that you can join our [Discord](https://discord.gg/duxWqxKk6J) community to ask questions. ## Engine Hooks Every time an agent needs to respond to a customer, the engine goes through a series of steps to generate the response. You can hook into these steps to modify the behavior of the engine. This is easily done by registering hook functions. While there are many hooks you can utilize, here's a simple example that: 1. Overrides the entire engine's response generation process if we detect that the customer only greeted the agent. 1. Inspects the agent's message for compliance breaches (using a custom checker) before sending it to the customer. ```python import asyncio from typing import Any import parlant.sdk as p async def intercept_message_generation_with_greeting( ctx: p.LoadedContext, payload: Any, exc: Exception | None ) -> p.EngineHookResult: if await is_only_greeting(ctx.interaction.last_customer_message): await ctx.session_event_emitter.emit_message_event( trace_id=ctx.tracer.trace_id, data="Hello! How can I help you today?", ) return p.EngineHookResult.BAIL # Intercept the rest of the process else: return p.EngineHookResult.CALL_NEXT # Continue with the normal process async def check_message_compliance( ctx: p.LoadedContext, payload: Any, exc: Exception | None ) -> p.EngineHookResult: generated_message = payload if not await is_compliant(generated_message): ctx.logger.warning(f"Prevented sending a non-compliant message: '{generated_message}'.") return p.EngineHookResult.BAIL # Do not send this message return p.EngineHookResult.CALL_NEXT # Continue with the normal process async def configure_hooks(hooks: p.EngineHooks) -> p.EngineHooks: hooks.on_acknowledged.append(intercept_message_generation_with_greeting) hooks.on_message_generated.append(check_message_compliance) return hooks async def main(): async with p.Server( configure_hooks=configure_hooks, ) as server: # Your logic here ... ``` ## Dependency Injection In order to extend the engine without modifying its source code, Parlant uses a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) system. This allows you to inject your own implementations of various components or even the processing engine itself (say, if you wanted to optimize the entire pipeline for particular use cases). For simplicity, we'll take a look at some basic extension mechanics, as well as common use cases for extension. However, if you need help with something that isn't covered here, please reach out to us on [Discord](https://discord.gg/duxWqxKk6J), [GitHub Discussions](https://github.com/emcie-co/parlant/discussions), or simply using the [Contact Page](https://parlant.io/contact) and we'll get back to you. ### Working with the Container Let's see how to work with Parlant's dependency injection container. The container is a central place where all components are registered, and you can use it to retrieve or register your own components. There are two things you might want to do with respect to the container: 1. **Register your own components**: You can add your own implementations of various components to the container, making them available for injection throughout the application. 1. **Adjust the behavior of existing components**: You can retrieve instances of components from the container, allowing you to use them in your own code. #### Registering Components Registering components lets you override nearly every aspect of how Parlant works. You can access the container during its registration phase by passing a `configure_container` hook to the server. This hook accepts a baseline state of the container, and returns a modified version of it before the server starts. ```python import asyncio import parlant.sdk as p async def configure_container(container: p.Container) -> p.Container: # Register your own components here # ... return container async def main(): async with p.Server( configure_container=configure_container, ) as server: # Your logic here ... ``` #### Adjusting Existing Components If you want to adjust the behavior of built-in components, you can retrieve them from the container and modify their behavior. This is useful for debugging or extending existing functionality without replacing the entire component. This hook is called `initialize_container`, and it allows you to modify components within the container after all of the classes have been registered and determined—but before the server actually starts to use them. This hook accepts the final state of the container, and returns `None`, as the container is only provided for _accessing_ registered components. ```python import asyncio import parlant.sdk as p async def initialize_container(container: p.Container) -> None: # Register your own components here # ... return container async def main(): async with p.Server( configure_container=configure_container, ) as server: # Your logic here ... ``` ## Open for Extension If you read or debug Parlant code, you'll come across many different types of components within the engine. Using the configuration and initialization hooks, you now know how to access them and extend, modify, or completely override their implementations as needed. #### Common Use Cases for Extensions 1. Overriding the no-match behavior of canned responses. This is actually documented here: [Canned Responses](https://parlant.io/docs/concepts/customization/canned-responses#no-match-responses). 1. Wrapping any engine component to add logging, monitoring, or other cross-cutting concerns. 1. Overriding the way particular guidelines are evaluated. For example, if they are simple and you have enough data, you can evaluate them with custom-trained BERT models instead of going through an LLM. 1. Overriding the entire message generation component, allowing you to leverage Parlant's guideline matching and tool execution, but using your message generation logic. But there's much more you can do. The engine is designed to be flexible and extensible, so you can adapt it to your specific needs without modifying the core codebase. ================================================ FILE: docs/advanced/explainability.md ================================================ # Enforcement & Explainability Let's dive into how Parlant enforces the conversation model consistently and provides visibility into your agent's situational understanding and decision-making process. In this section, you'll learn: 1. How _Attentive Reasoning Queries (ARQs)_ enforce the conversation model 1. How to use ARQ artifacts to troubleshoot and improve behavior ### Understanding Runtime Enforcement During message generation, Parlant ensures guidelines are followed consistently in real-time conversations through our [Attentive Reasoning Queries](https://arxiv.org/abs/2503.03669#:~:text=We%20present%20Attentive%20Reasoning%20Queries%20%28ARQs%29%2C%20a%20novel,in%20Large%20Language%20Models%20through%20domain-specialized%20reasoning%20blueprints.) prompting method. Rather than simply adding guidelines to prompts and hoping for the best, Parlant employes explicit techniques to maximize the LLM's ability and likelihood to adhere to your guidelines. Attentive Reasoning Queries (ARQs) are essentially structured reasoning blueprints built into prompts that guide LLMs through specific thinking patterns when making decisions or solving problems. Rather than hoping an AI agent naturally considers all important factors, ARQs explicitly outline reasoning steps for different domains—like having specialized mental checklists to go through. What makes ARQs effective for behavioral enforcement is that they force attention on critical considerations that might otherwise be overlooked. The model must work through predetermined reasoning stages (like context assessment, solution exploration, critique, and decision formation), ensuring it consistently evaluates important constraints before taking action. ![ARQs](https://arxiv.org/html/2503.03669v1/x1.png) **Figure:** Illustration of ARQs (taken from the [research paper](https://arxiv.org/abs/2503.03669#:~:text=We%20present%20Attentive%20Reasoning%20Queries%20%28ARQs%29%2C%20a%20novel,in%20Large%20Language%20Models%20through%20domain-specialized%20reasoning%20blueprints.)) Besides increasing accuracy and conformance to instructions, this process creates, as a byproduct, transparent, auditable reasoning paths that help maintain alignment with desired behaviors. ARQs are flexible enough to adapt to different contexts and risk levels, with reasoning blueprints that can be tailored to specific domains or regulatory requirements. While there's some computational overhead to this more deliberate thinking process, carefully designed ARQs can beat Chain-of-Thought reasoning in both accuracy and latency. Parlant uses different sets of ARQs for each of its components (e.g., guideline matching, tool-calling, or message composition), and dynamically specializes the ARQs to the specific entity it's evaluating, whether it's a particular guideline, tool, or conversational context. Here's an illustrated example from the `GuidelineMatcher`'s logs: ```json { "guideline_id": "fl00LGUyZX", "condition": "the customer wants to return an item", "condition_application_rationale": "The customer explicitly stated that they need to return a sweater that doesn't fit, indicating a desire to return an item.", "condition_applies": true, "action": "get the order number and item name and them help them return it", "action_application_rationale": [ { "action_segment": "Get the order number and item name", "rationale": "I've yet to get the order number and item name from the customer." }, { "action_segment": "Help them return it", "rationale": "I've yet to offer to help the customer in returning the item." } ], "applies_score": 9 } ``` ### Explaining and Troubleshooting Agent Behavior Message generation in Parlant goes through quite a lot of quality assurance. As mentioned above, ARQs produce artifacts that can help explain how the agent interpreted circumstances and instructions. When you run into issues, you can inspect these artifacts to better understand why the agent responded the way it did, and whether it correctly interpreted your intentions. Over time, this feedback loop helps you build more precise and effective sets of guidelines. ![Explainability in Parlant](https://parlant.io/img/explainability.gif) ================================================ FILE: docs/concepts/customization/canned-responses.md ================================================ # Canned Responses Canned responses provide you with precise control over your Parlant agent's responses. The [concept of canned responses](https://en.wikipedia.org/wiki/Canned_response) comes from real-world contact centers, where they're widely used to ensure that agents communicate with customers in a consistent, accurate, and brand-aligned manner. By restricting to a pre-defined and pre-approved set of responses, you ensure your agent communicates with a consistent tone, style, and accuracy, aligning perfectly with your brand voice and service protocols while **completely eliminating the risk of even subtle unwanted or hallucinated outputs**. Canned responses function like a hand of cards: given a set of choices you provide to your agent, it selects the most appropriate "card" that best matches the required response based on the conversation context. ![Cartoon1](https://parlant.io/img/utterance_cartoon_1.png) ![Cartoon2](https://parlant.io/img/utterance_cartoon_2.png) ## A Practical Example #### Without Canned Responses Using LLM-generated (token-by-token) responses. > **Customer:** Do you have it in stock? > > **Agent:** Yes, we've got this item in stock! Let me know if you need any help finding it. #### With Canned Responses ``` # Draft message: "Yes, we've got this item in stock! Let me know if you need any help finding it." # # Available templates: # - ... # - "Hey, {{std.customer.name}}! What help do you need today?" # - ... # - "No, sorry, we've just sold the last ones. Would you like to see something similar?" # - "Yep, we have it. Should I add it to your cart?" # - ... ``` > **Customer:** Do you have it in stock? > > **Agent:** Yep, we have it. Should I add it to your cart? ## How Canned Responses Work Under the hood, canned responses work in a 4-stage process: 1. The agent drafts a fluid message based on its current situational awareness (interaction, guidelines, tool results, etc.) 2. The engine retrieves the most relevant canned response templates, based on the draft message 3. The engine renders the candidate templates, using tool-provided field substitutions where applicable 4. Based on the draft message, the agent selects the most fitting canned response out of the provided candidates ```mermaid flowchart LR A[Draft Fluid Message] --> B[Retrieve Most Relevant Response Templates] --> C[Render Templates] --> D[Select Canned Response] ``` ## Composition Modes Parlant agents can use one of several **composition modes** in their responses. These composition modes offer varying levels of restriction on the agent's outputs, as well as the manner in which it uses your canned responses. | Mode | Description | Use Cases | |------|-------------|-----------| | **Fluid** | The agent prioritizes selecting from canned responses if a good match can be found, but falls back to default message generation otherwise. | **(A)** Staying mostly fluid, but controlling specific situations and responses where applicable
**(B)** Prototyping an agent, generating fluid recommendations for additional utterances as you go | | **Composited** | The agent will only use the canned response candidates to alter the generated draft message so as to mimic the style of the retrieved candidates | Brand-sensitive use cases where tone of voice is important to maintain | | **Strict** | The agent can only output responses from the provided ones. If no matching response exists, the agent will send a customizable no-match message. | High-risk settings that cannot afford even the most subtle and infrequent hallucinations | > **Tip:** > If you have a high-risk use case and are apprehensive about deploying GenAI agents to your customers, we recommend starting out with strict mode. Parlant is flexible and will allow you to easily transition to more fluid modes when you're ready. You will still maintain and utilize all other aspects of your conversation model as you switch between composition modes. ### Setting an Agent's Composition Mode You just need to pass the right `composition_mode` when creating your agent: ```python await server.create_agent( name="My Agent", description="An agent that uses canned responses", composition_mode=p.CompositionMode.STRICT, # or FLUID or COMPOSITED ) ``` ## Creating Canned Responses Here's how you can create canned responses for your agent: ```python await agent.create_canned_response(template=TEXT) ``` #### Journey-Scoped Responses You can also add **journey-scoped** responses. These are responses that are only available when a specific journey is active. Scoping your canned responses to journeys is useful for narrowing down the set of responses to choose from, increasing the chances of choosing the desired response. To do so, just call the `create_canned_response` method on a specific journey instance instead of the agent: ```python await journey.create_canned_response(template=TEXT) ``` #### Preamble Responses Parlant's employs multiple techniques to enhance the conversational user-experience by leveraging the principle of [perceived performance](https://en.wikipedia.org/wiki/Perceived_performance#:~:text=Perceived%20performance%2C%20in%20computer%20engineering%2C%20refers%20to%20how,The%20concept%20applies%20mainly%20to%20user%20acceptance%20aspects.). One of these techniques is the use of **preamble responses**. Parlant agents often send preamble responses (such as _"Got it."_, _"Understood."_, or _"Let me look into that"_) to acknowledge the customer's input while it's working on generating a detailed, accurate response. Normally, these preamble responses are automatically generated by the agent according to the context, but you can also create custom preamble responses for the agent to choose from. To create a canned preamble response, just add the `preamble()` tag in your canned response creation: ```python await agent.create_canned_response( template="Sure thing.", tags=[p.Tag.preamble()], ) ``` ## Template Syntax Canned responses are defined using **templates**. Templates are strings that can contain static text, as well as dynamic fields that will be substituted with actual values when the response is selected. ### Standard Fields Use standard fields (using the `std.` prefix) to display dynamic information from the conversation context: #### Available values 1. `std.customer.name`: String; The customer's name (or `Guest` for a non-registered [customer](https://parlant.io/docs/concepts/entities/customers)) 2. `std.agent.name`: String; The agent's name 3. `std.variables.NAME`: Any; The content of a variable named `NAME` 4. `std.missing_params`: List of strings; Contains the names of missing tool parameters (if any) based on [Tool Insights](https://parlant.io/docs/concepts/customization/tools#tool-insights-and-parameter-options) #### Example ```python await agent.create_canned_response( template="Hi {{std.customer.name}}, Yes, this product is available in stock." ) ``` ### Generative Fields If you refer to a field with a `generative.` prefix, the LLM will auto-infer and substitute the value based on its name and the surrounding context. This is a great way to introduce controlled, localized generation into strict templates. #### Example ```python await agent.create_canned_response( template="Can I ask why you'd like to return {{generative.item_name}}?" ) ``` ### Tool/Retriever-Based Fields Canned responses can refer to fields coming from tool and retriever results. These fields must be specified in the `canned_response_fields` property of a `ToolResult` or a `RetrieverResult`. This is one of the most useful field types as they can introduce truly dynamic data into your canned responses. > **Warning: The Crucial Role of Fields in Avoiding Consequential Hallucinations** > > There's another great benefit to using tool-based fields in your canned responses. When retrieving candidate responses, the engine will also look at the `canned_response_fields` to determine relevance. > > Responses that refer to fields that aren't present in the context will never be selected, even if they're similar to the draft message. This is important, as it helps to ensure that the responses generated by the agent are grounded in the actual data available. > > For example, when using the strict composition mode, your agent could never output a message referring to a `{{successful_transaction.id}}` if the `successful_transaction` field was not provided by a successfully-run tool call. In other words, if you coordinate your responses and tools correctly, you can ensure your agent never hallucinates misleading responses about data or state. #### Example ```python @p.tool def get_account_balance(context: p.ToolContext) -> p.ToolResult: balance = 1234.5 return p.ToolResult( # Note that you must still provide the result in the `data` field, # as this is what will inform the agent when evaluating guidelines, # calling tools, as well as when generating the draft message. data={f"Account balance is {balance}"}, # Here you provide dynamic values specifically for template field substitution canned_response_fields={"account_balance": balance}, ) ``` And this is how your response template would refer to this field: ```python await agent.create_canned_response(template="Your current balance is {{account_balance}}") ``` ## Returning Full Responses from Tools Tools can also return full canned responses for consideration. This is useful when you want to generate a complete response based on the tool's output, rather than just providing data for field substitution. It is usually particularly relevant for complex Q&A retrieval scenarios. ```python @p.tool def get_answer(context: p.ToolContext, question: str) -> p.ToolResult: answer = "The answer to your question is...." return p.ToolResult( data=answer, # Make the answer available as a complete canned response candidate canned_responses=[answer], ) ``` ## Optimizing Response Selection Let's look at how you can ensure that your agent selects the right canned response. #### Controlling the Draft Message Because of how the selection process works, the first step to getting the right response delivered is to ensure that the draft message is generated as closely as possible to your desired response. You can achieve this using all of the standard control mechanisms, such as guidelines, journeys, tools, glossary terms, and agent description. This means you'll need to keep a close eye on what drafts are being generated prior to response selection. You can inspect the generated draft message in the integrated UI to see what the agent wanted to say. ![View canned response draft](https://parlant.io/img/utterance_draft_demo.gif) #### Ensuring the Right Candidates Are Retrieved Next, you need to ensure that your desired response templates are retrieved by the engine as candidates for selection. Sometimes, the response itself is close enough to the draft message to appear in the candidate list. But not always—especially if its template contains field substitutions which make semantic similarity comparisons harder. For this, you can use **signals** when creating canned responses. Signals are a way to tell your agent, "This response is a good match for these drafts." Each signal is essentially a draft message example. When retrieving candidate responses, the engine will also look at these signals to find determine relevance, so that if a response has a signal that's really close to the draft message, it will be retrieved as a candidate even if the response itself is quite different in form. Here's how you can add signals to your canned responses: ```python await agent.create_canned_response( template="Yes, we've got this item in stock! Let me know if you need any help finding it.", signals=["We do have it in stock", "We do! Do you need help finding it?"], ) ``` ## The Flexibility of Jinja2 Response templates integrate with the Jinja2 templating engine, enabling more dynamic formatting, substitution filters, as well as list processing. You can learn more advanced syntax on the [Jinja2 documentation site](https://jinja.palletsprojects.com/en/stable/). #### Example ```python @p.tool def get_pizza_toppings(context: p.ToolContext) -> p.ToolResult: toppings = ['olives', 'peppers', 'onions'] return p.ToolResult( data={f"Toppings are {toppings}"}, canned_response_fields={"toppings": toppings}, ) ``` ```python await agent.create_canned_response( template="We have the following toppings {% for t in toppings %}\n- {{t}}{% endfor %}" ) ``` ## No-Match Responses When using strict composition mode, if the agent cannot find a suitable canned response for its draft, it will send a no-match response. If you want to customize this response, there are two ways to do so: 1. Customizing the static no-match response 2. Using a custom no-match provider to dynamically generate the response according to the context #### Static No-Match Response This is the simplest way to customize the no-match response. You can set a static no-match response that will be used whenever the agent cannot find a suitable canned response. ```python async def initialize_func(c: p.Container) -> None: no_match_provider = c[p.BasicNoMatchResponseProvider] no_match_provider.template = "My custom no-match response." async with p.Server( initialize_container=initialize_func, ) as server: ... ``` #### Custom No-Match Provider If you need more flexibility, you can create a custom no-match response provider. This allows you to generate the no-match response dynamically based on the conversation context. However, please note that `p.LoadedContext` (which gives you access to internal engine state) is subject to change in future releases, so keep in mind you may need to adjust your implementation accordingly at some point. ```python class CustomNoMatchResponseProvider(p.NoMatchResponseProvider): async def get_template(self, context: p.LoadedContext, draft: str | None) -> str: # Generate a custom no-match response based on the provided context, # such as the conversation history, draft message, guidelines, tool calls, etc. template = "..." return template async def configure_func(c: p.Container) -> p.Container: c[p.NoMatchResponseProvider] = CustomNoMatchResponseProvider() async with p.Server( configure_container=configure_func, ) as server: ... ``` ================================================ FILE: docs/concepts/customization/glossary.md ================================================ # Glossary The glossary is a fundamental part of shaping your agent's understanding of its domain. It's like your agent's professional dictionary: a set of terms specific to your business or service context. ### When to Use the Glossary When you create an agent to handle specific tasks, it often needs to understand the unique vocabulary of your domain. For example, if your agent helps guests book rooms at the Boogie Nights hotel, it needs to know what "Boogie Nights" means in your context—in this case, that it's not just a movie title, but your hotel's name. #### Creating Glossary Terms Here's how to create a new glossary term: ```python await agent.create_term( name=TERM, description=DESCRIPTION, synonyms=[SYNONYM_1, SYNONYM_2, ...], ) ``` ### Structure of Terms Each glossary entry consists of three components: > * **Term:** The word or phrase being defined > * **Description:** What this term means in your specific context > * **Synonyms:** Alternative ways users might refer to this term For example: ```python await agent.create_term( name="Boogie Nights", description="Our luxury beachfront hotel located in Miami", synonyms=["BN Hotel", "The Boogie", "Boogie Hotel"], ) ``` ### How Agents Use the Glossary The glossary serves two crucial purposes in agent interactions. First, it helps your agent understand customers better when they interact with it. When a guest says _"I'd like to stay at The Boogie,"_ the agent knows they're referring to your hotel. Second, it helps the agent interpret your guidelines correctly. Consider the following configuration: ```python await agent.create_guideline( condition="the user asks about Ocean View rooms", action="explain the Sunrise Package benefits", ) await agent.create_term( name="Ocean View", description="Our premium rooms on floors 15-20 facing the Atlantic", synonyms=["seaside rooms", "beach view"], ) await agent.create_term( name="Sunrise Package", description="Complimentary breakfast and early check-in for Ocean View bookings", synonyms=["morning special", "sunrise special"], ) ``` Here, both the condition as well as the action depend on the agent understanding what these terms mean. If the Customer comes in and asks, > **Customer:** I heard you have some rooms with a view to the Atlantic. What are those? The agent can understand, based on the glossary term, that the condition _"the user asks about Ocean View rooms"_ is met, and it can then respond with the action _"explain the Sunrise Package benefits"_. ## Glossary vs Guidelines vs Agent Description Each component serves a distinct purpose in shaping your agent's behavior: 1. The Glossary teaches your agent "what things are". For example, _"A Club Member is a guest who has stayed with us more than 5 times."_ You can have as many terms as you want. 1. Guidelines teach your agent "how to act in situations". For example, _"When speaking with Club Members, acknowledge their loyalty status."_ You can have as many guidelines as you want. 1. Agent Description provides overall context and personality. For example, _"You are a helpful hotel booking assistant for Boogie Nights."_ The agent's description is static and limited. Think of it this way: the glossary builds your agent's vocabulary, guidelines shape its behavior, and the agent description sets its overall context, role, personality and tone. ## Glossary vs Tools While both glossary terms and tools help your agent understand your domain, they serve fundamentally different purposes. The glossary provides static knowledge, while tools enable dynamic data access. Consider a hotel booking scenario: **Glossary Term:** > * **Term:** Club Member > * **Description:** A guest who has stayed with us more than 5 times > * **Synonyms:** loyal guest, regular guest **Tool:** `check_member_status(user_id) # Returns current stay count and benefits` The glossary term provides a consistent definition of what a Club Member is, while the tool can check a specific user's actual status in your database. Similarly: **Glossary Term:** > * **Term:** Ocean View Room > * **Description:** Premium rooms on floors 15-20 facing the Atlantic > * **Synonyms:** seaside room, beach view **Tool:** `check_room_availability(room_type, dates) # Returns current availability and rates` The glossary helps your agent understand what an Ocean View Room is, while the tool provides real-time information about specific rooms' availability and pricing. This separation between static knowledge (glossary) and dynamic data access (tools) helps create clear, maintainable agent implementations that can handle both general inquiries and specific, data-driven interactions. ================================================ FILE: docs/concepts/customization/guidelines.md ================================================ # Guidelines Guidelines are a powerful customization feature. While they're quite simple in principle, there is a lot to say about them. ### What Are Guidelines? Guidelines are the primary way to nudge the behavior of [agents](https://parlant.io/docs/concepts/entities/agents) in Parlant in a contextual and targeted manner. They allow us to instruct how an agent should respond in specific situations, overriding its default behavior, thus ensuring that its behavior aligns with our expectations and business needs. Guidelines allow us to shape an [agent](https://parlant.io/docs/concepts/entities/agents)'s behavior in two key scenarios: 1. When certain out-of-the-box responses don't meet our expectations 1. When we simply want to ensure consistent behavior across all interactions > **Guidelines vs. Journeys** > > Journeys are the ideal way to provide a structured, step-by-step interaction flow, while guidelines are more about providing contextual nudges to the agent's behavior. Use journeys for complex interactions that require multiple steps, and guidelines for simpler, context-specific adjustments—as well as for simple, general tool-calling triggers that aren't necessarily within any particular journey. #### Example Suppose we have an agent that helps customers order products. By default, the agent's behavior might look like this: > **User:** I'd like to order a new laptop. > > **Agent:** Sure, what are your preferences? (e.g., budget, operating system, screen size, use cases?) But say we want to make our agent more personable by first having it ask simply whether they want Mac or Windows. We can add a guideline to ensure that this happens consistently, like so: ```python await agent.create_guideline( condition="The customer wants to buy a laptop", action="First, determine whether they prefer Mac or Windows" ) ``` Resulting in a conversation like this: > **User:** I'd like to order a new laptop. > > **Agent:** Sounds good. Would you prefer Mac or Windows? > **Careful What You Wish For** > > Instructing an LLM is very similar to instructing a human, except that by default it has absolutely zero context of who is instructing it and the context in which the instruction is given. For this reason, when we provide guidelines, we must strive to be as clear and articulate as possible, so that the agent can follow them without ambiguity. More about this later in this page. ### The Structure of Guidelines In Parlant, each guideline is composed of two parts: the **condition** and the **action**. 1. The **action** part describes what the guideline should accomplish. For example, "Offer a discount." 1. The **condition** is the part the specifies _when the action should take place_. For example, "It is a holiday". ```python await agent.create_guideline( condition="It is a holiday", action="Offer a discount on the order" ) ``` When speaking informally about guidelines, we often describe them in _when/then_ form: When , Then , or in this case, When it is a holiday, Then offer a discount. > **Guideline Tracking** > > Once the action is accomplished in a session, Parlant will deactivate the guideline—unless it has reason to believe the action should re-apply due to a contextual shift (e.g., in the example above, if the customer starts another order). ### Using Tools One of the foremost issues with most LLMs is their bias toward false-positives. Put simply, they are always looking to please, so they will tend to answer positively to most questions. This becomes a huge problem when we want to ensure that an agent only performs certain actions when it has the right context or information. For this reasons, Parlant allows us to associate [tools](https://parlant.io/docs/concepts/customization/tools) (essentially, functions) with guidelines, such that the agent would only consider calling a tool when a guideline's requisite condition is met within the interaction's current context. Just as importantly, it also allows you to specify contextual information on *how* and *why* you want a particular tool to be called when certain circumstances hold. Here's an example: ```python @p.tool async def find_products_in_stock(context: p.ToolContext, query: str) -> p.ToolResult: ... await agent.create_guideline( condition="The customer asks about the newest laptops", action="First recommend the latest Mac laptops", # The guideline's action will ensure the following tool is # called with the right query (e.g., "Latest Mac laptops") tools=[find_products_in_stock], ) ``` ## How Guidelines Work To understand how guidelines work, we need to look briefly at Parlant's response processing pipeline. When an agent receives a message, it goes through a response processing pipeline that involves several steps to ensure the response is aligned with the guidelines and expectations. ```mermaid graph LR Engine -->|Match guidelines| GuidelineMatcher GuidelineMatcher -->|Call associated tools| ToolCaller ToolCaller -->|Compose message| MessageComposer MessageComposer -.->|Generated response| Engine ``` As the figure above suggests, guidelines are evaluated and matched *before* the agent composes its response. > **Keep in Mind** > > This means that the agent needs to be able to evaluate and apply instructions and tool calls based on the interaction's context *before* generating the response. In other words, guidelines such as "Do X immediately after you've done Y" may not work as you expect. ### How Parlant Uses Guidelines LLMs are a magnificent creation, built on the principle of [statistical attention](https://arxiv.org/abs/1706.03762) in text; yet, their attention span is painfully finite. When it comes to following instructions, they need help. Behind the scenes, Parlant ensures that agent responses are aligned with expectations by dynamically managing the LLM's context to only include the relevant guidelines at each point. ```mermaid %%{init: {'sequence': {'mirrorActors': false}}}%% sequenceDiagram participant Engine participant GuidelineMatcher participant MessageComposer Engine ->> GuidelineMatcher: match guidelines GuidelineMatcher -->> Engine: Engine ->> MessageComposer: compose contextually guided message MessageComposer -->> Engine: ``` Before each response, Parlant only loads the guidelines that are relevant to the conversation's current state. This dynamic management keeps the LLM's "cognitive load" minimal, maximizing its attention and, consequently, the alignment of each response with expected behavior. > Another important ability that Parlant employs to ensure alignment is supervising the agent's outputs before they reach the [customer](https://parlant.io/docs/concepts/entities/customers), to ensure to the utmost degree that guidelines were correctly adhered to. To achieve this, NLP researchers working on Parlant have devised an innovative prompting technique called **Attentive Reasoning Queries (ARQs)**. You're welcome to explore the research paper on [arxiv.org, Attentive Reasoning Queries: A Systematic Method for Optimizing Instruction-Following in Large Language Models](https://arxiv.org/abs/2503.03669#:~:text=We%20present%20Attentive%20Reasoning%20Queries%20%28ARQs%29%2C%20a%20novel,in%20Large%20Language%20Models%20through%20domain-specialized%20reasoning%20blueprints.). ### Managing Guidelines Parlant is built to make guideline management as simple as possible. Often, guidelines are added when business experts request behavioral changes in the agent. Developers can use Parlant to make those changes, iterating quickly and reliably, at the pace of the business experts they're working with. Here's a practical example. When Sales requests: "The agent should first ask about the customer's needs and pain points before discussing our solution," implementing this feedback takes just a minute by adding the following: ```python await agent.create_guideline( condition="The customer has yet to specify their current pain points", action="Seek to understand their pain points before talking about our solution" ) ``` Once added, Parlant takes care of the rest, automatically ensuring this new guideline is followed consistently across all relevant conversations, without degrading your agent's conformance to other guidelines. ### Formulating Guidelines Think of an LLM as a highly knowledgeable stranger who's just walked into your business. They might have years of general experience, but they don't know your specific context, preferences, or way of doing things. Yet, this stranger is eager to help and will always try to—even when uncertain. This is where guidelines come in. They're your way of channeling this endless enthusiasm and broad knowledge into focused, appropriate responses. But specifying effective guidelines is a bit of an art—just like it is with people. #### The Art of Guidance Consider a customer service scenario. As a very naive example, we might be tempted to have: **DON'T** > * **Condition:** Customer is unhappy > * **Action:** Make them feel better While well-intentioned, this is an example of a guideline that is just too vague. The LLM might interpret this in countless ways, from offering discounts it can't actually provide to making jokes that might be inappropriate for your brand. Instead, consider: **DO** > * **Condition:** Customer expresses dissatisfaction with our service > * **Action:** Acknowledge their frustration specifically, express sincere empathy, and ask for details about their experience so we can address it properly. Notice how this guideline is both specific and bounded. **DON'T** > * **Condition:** Customer asks about products > * **Action:** Recommend something they might like **DO** > * **Condition:** Customer asks for product recommendations without specifying preferences > * **Action:** Ask about their specific needs, previous experience with similar products, and any particular features they're looking for before making recommendations #### Finding the Right Balance In principle, we're looking for guidelines that are "just right"—neither over nor under specified. Consider these iterations for a technical support agent: **DON'T** Too vague: > * **Condition:** Customer has a technical problem > * **Action:** Help them fix it **DON'T** Too rigid: > * **Condition:** Customer reports an error message > * **Action:** First ask for their operating system version, then their browser version, then their last system update date **DO** Just right: > * **Condition:** Customer reports difficulty accessing our platform > * **Action:** Express understanding of their situation, ask for key details about their setup (OS and browser), and check if they've tried some concrete troubleshooting steps Remember, LLMs will usually take your guidance quite literally. If you tell your agent to "always suggest premium features," it might do so even when talking to a customer who's complaining about pricing. Always try to consider the broader context and potential edge cases when formulating your guidelines. It'll pay off in less changes and troubleshooting down the line. **If in doubt, prefer to err on the side of vagueness.** The goal of Agentic Behavior Modeling isn't to script out every possible interaction but to provide clear, contextual guidance that shapes the LLM's natural generalization abilities into reliable, appropriate responses for your specific use case. ================================================ FILE: docs/concepts/customization/journeys.md ================================================ # Journeys There are many use cases where you want your agent to follow a specific flow of conversation, such as booking a trip, troubleshooting an issue, or otherwise guiding a user through a conversational process in the intended manner. In Parlant, this can be achieved easily and reliably using **Journeys**. #### Journey Structure Journeys have 4 important components: 1. **Title:** A short, descriptive name for the journey, to differentiate it from other journeys. 1. **Conditions:** These contextual queries determine when a journey should be active. 1. **Description:** This lets you describe the nature of the journey, including motivating or orientating notes, if needed. 1. **States & Transitions**: A state diagram that communicates to the agent what the ideal flow is. > **Balancing Rigidity vs. Flexibility** > > In traditional conversational frameworks, flows are rigidly defined, with each state and transition being strictly followed to the letter. > > While this type of approach is easy to reason about and implement, it very often leads to frustrating user experiences when the agent is unable to adapt to the customer's interaction patterns. This "one-size-fits-all" approach doesn't account for the nuances of human conversation—leading to user disengagement and dissatisfaction, and ultimately resulting in an unused agent. > > Parlant implements a "lessons learned" approach, allowing agents to traverse the journey's states in a more natural way. They can choose to skip states, revisit previous states, or even jump ahead to later states in an adaptive manner. > > As such, journeys are not meant to be followed rigidly, but rather to serve as a guiding framework for the agent. The agent will strive to follow the flow as strictly as it can, while still maintaining an adaptive approach toward the customer's interaction patterns. ## A Worked Example Consider the following example for a journey in a travel agent: > * **Title:** Book Flight > * **Conditions:** The customer requested to book a flight > * **Description:** This journey guides the customer through the flight booking process. ```mermaid %%{init: { "theme": "forest" }}%% stateDiagram-v2 direction LR state "Where to?" as A state "Dates?" as B state "Load destinations" as Ca state "Suggest" as Cb state "Confirm" as E state "Book" as F state "Provide ticket" as G [*] --> A A --> Ca: Don't know Ca --> Cb A --> B: Destination provided Cb --> B: Destination selected B --> E E --> F: Yes E --> [*]: No F --> G G --> [*] style Ca fill:#ffeecc,stroke:#333,stroke-width:1px style F fill:#ffeecc,stroke:#333,stroke-width:1px ``` This journey will be activated when the customer asks to book a flight. The agent will then strive to follow the flow while maintaining an adaptive approach at the pace of the customer, yet ensuring that all necessary information is collected. #### Implementing the Journey Before we learn more about how journeys work, let's look at how we would implement the journey above: ```python async def create_book_flight_journey(agent: p.Agent): journey = await agent.create_journey( title="Book Flight", conditions=["The customer requested to book a flight"], description="This journey guides the customer through the flight booking process.", ) t1 = await journey.initial_state.transition_to(chat_state="Ask if they have a destination in mind") # Branch out based on the customer's response t2 = await t1.target.transition_to(condition="They do", chat_state="Get dates of travel") t3a = await t1.target.transition_to(condition="They don't", tool_state=load_popular_destinations) t3b = await t3a.target.transition_to(chat_state="Recommend a destination") # Merge back to the main path after choosing a destination. # This is done by transitioning into an existing state node. await t3b.target.transition_to(state=t2.target, condition="Destination selected") t4 = await t2.target.transition_to(chat_state="Confirm details") t5a = await t4.target.transition_to(tool_state=book_flight) t5b = await t5a.target.transition_to(chat_state="Provide ticket details") ``` ## States and Transitions A journey is modeled after a state diagram, which is a directed graph in which each node represents a **state** and each edge represents a **transition** (which may be associated with a condition). ```mermaid stateDiagram-v2 direction LR state "CHAT STATE" as A state "TOOL STATE" as B state "CHAT STATE" as C state "CHAT STATE" as D state "FORK STATE" as E state "CHAT STATE" as F state "CHAT STATE" as G [*] --> A: INITIAL A --> B: CONDITIONAL A --> C: CONDITIONAL B --> D: DIRECT C --> E: DIRECT D --> E: DIRECT E --> F: CONDITIONAL E --> G: CONDITIONAL F --> [*]: END G --> [*]: END style B fill:#ffeecc,stroke:#333,stroke-width:1px ``` #### States 1. **Chat States:** While in this state, the agent will chat with the customer while being guided by the state's action. The agent may spend multiple turns in this state, until it decides to transition to another state. ```python t = await state.transition_to(chat_state=CONVERSATIONAL_INSTRUCTION) ``` 2. **Tool States:** In this state, the agent will call an external tool to perform an action and load its result into the context. A tool state must be followed by a chat state, which will usually be used to present the tool's result to the customer. ```python t = await state.transition_to(tool_state=TOOL) ``` ```python t = await state.transition_to(tool_state=TOOL, tool_instruction=OPTIONAL_HINT_ON_HOW_TO_USE_TOOL) ``` > **Transitioning from Tool to Chat** > > When transitioning from a tool state to a chat state, the agent will automatically load the tool's result into the context, so you can use it in the chat state's action. Note that a tool state cannot transition to another tool state; it must always be followed by a chat state. > > This is by design, as tool usage can incur noticeable latency in agentic applications. Instead of using sequential tool states, you should use a single tool state to perform all necessary actions, and then follow it with a chat state to present the results to the customer. #### Transitions 1. **Direct Transitions:** These transitions should always be taken. They move the conversation forward without branching. 2. **Conditional Transitions:** These transitions are only taken if/when their associated condition is met. ```python t = await state.transition_to(chat_state=CONVERSATIONAL_INSTRUCTION, condition=CONDITION) ``` ```python t = await state.transition_to(tool_state=TOOL, condition=CONDITION) ``` In most cases, you'd be using the `transition_to()` overload that takes a `chat_state` or `tool_state` argument, which will automatically create the transition's target state for you. However, you can also use the `transition_to()` overload that takes a `state` argument, which allows you to transition to an existing state node in the journey. ```python t = await state.transition_to(state=EXISTING_STATE) ``` ```python t = await state.transition_to(state=EXISTING_STATE, condition=CONDITION) ``` > **Combining Conditional and Direct Transitions** > > If a state has a conditional transition to another state, it cannot also have a direct transition coming out of it. This is because the engine would not be able to logically determine which transition to take when the condition is met. The SDK enforces this rule. #### Fork States Journeys also support a special kind of state, called a **fork state**. In this state, the agent will evaluate conditions and branch the conversation flow accordingly. While, strictly speaking, such branching can be modeled without fork states, they are sometimes a useful modeling tool for keeping the conversation flow clear, explicit, and organized. ```python fork = await state.fork() t1 = await fork.transition_to(chat_state=CONVERSATIONAL_INSTRUCTION, condition=CONDITION_1) t2 = await fork.transition_to(chat_state=CONVERSATIONAL_INSTRUCTION, condition=CONDITION_2) t3 = await fork.transition_to(tool_state=TOOL, condition=CONDITION_3) ``` > **Visualizing Your Journey** > > Building a state diagram in code can sometimes be a bit confusing. It's often useful to visualize the journey as you build it, to ensure that the flow is clear, logical, and as you intend. Here's how it's done: > > 1. Visit `http://localhost:8800/journeys` in your browser. > 2. Copy the ID of the journey you want to visualize. > 3. Visit `http://localhost:8800/journeys//mermaid` in your browser, replacing `` with the ID you copied. > 4. Copy the generated Mermaid diagram code. > 5. Paste it into a [Mermaid live editor](https://mermaid.live/) to visualize the journey. ## Journey vs. Task Automation If you look at how the engine works with journeys, it means that journeys should not be used to guide the model on how to automate tasks. Instead, journeys are used by the agent to self-orientate and guide the conversation flow according to your preferences. This is a good time to recall the importance of separating **business logic** from **conversation logic**. The former is best handled by custom, [dedicated tools](https://parlant.io/docs/concepts/customization/tools) (which may or may not use LLMs internally), while the latter is best handled by the conversational engine. #### Do's and Don'ts **DON'T** The following is ***not*** a valid journey, as it does not represent a conversation flow but rather a task automation flow. ```mermaid stateDiagram-v2 direction LR state "Find user ID" as A state "Load personal preferences" as B state "Send email" as C [*] --> A A --> B B --> C: Email notifications enabled B --> [*]: Email notifications disabled style A fill:#ffeecc,stroke:#333,stroke-width:1px style B fill:#ffeecc,stroke:#333,stroke-width:1px style C fill:#ffeecc,stroke:#333,stroke-width:1px ``` **DO** The following is a valid journey, as it represents a conversation protocol that guides the customer through a process. ```mermaid stateDiagram-v2 direction LR state "Ask for order number" as A state "Get order details" as B state "Process refund" as C state "Transfer to human" as D [*] --> A A --> B B --> C: Eligible for refund B --> D: Not eligible for refund C --> [*] D --> [*] style B fill:#ffeecc,stroke:#333,stroke-width:1px ``` ## Context Management LLMs are a magnificent creation, built on the principle of [statistical attention](https://arxiv.org/abs/1706.03762) in text; yet, their attention span is painfully finite. When it comes to following instructions, they need help. Behind the scenes, Parlant ensures that agent responses are aligned with expectations by dynamically managing the LLM's context to only include the relevant journeys at each point. It does this using the `GuidelineMatcher`, essentially matching the current conversation context with the relevant journeys' conditions—which, behind the scenes are basically observational (non-actionable) guidelines. ```mermaid %%{init: {'sequence': {'mirrorActors': false}}}%% sequenceDiagram participant Engine participant GuidelineMatcher participant JourneyStore participant MessageComposer Engine ->> GuidelineMatcher: match guidelines GuidelineMatcher -->> Engine: Engine ->> JourneyStore: get journeys for matched conditions JourneyStore -->> Engine: Engine ->> GuidelineMatcher: match journey states GuidelineMatcher -->> Engine: Engine ->> MessageComposer: MessageComposer -->> Engine: ``` Before each response, Parlant only loads the guidelines and journeys that are relevant to the conversation's current state. This dynamic management keeps the LLM's "cognitive load" minimal, maximizing its attention and, consequently, the alignment of each response with expected behavior. > **Latency Optimizations** > > This back and forth approach is implemented in an optimized algorithm that minimizes response latency. > > The engine first tries to predict which journeys will be activated based on the current conversation context. Given this prediction, it attempts to match the relevant journeys' states in parallel to guideline matching, shaving seconds off the response latency. > > Only when this prediction fails (i.e., when other journeys were activated) does it incur the extra step to match their states, as well. ## Journey-Scoped Guidelines You can add journey-scoped [guidelines](https://parlant.io/docs/concepts/customization/guidelines) that can only be activated when their dependent journeys are also active. At all other times, these guidelines would be ignored. Using journey-scoped guidelines is the recommended way to handle digressions from the journey's main flow in deliberate ways. It also helps you maintain a clean and organized conversation model, ensuring that certain guidelines are only evaluated and activated in their intended context. > **Instruction Precedence** > > Please note that, in general, Parlant agents give more weight to guidelines than to journey states, as guidelines are treated as more specific behavioral overrides. This means that, if a guideline is matched, it will tend to take precedence over the active journey states. ```python @p.tool async def transfer_to_human_agent(context: p.ToolContext) -> p.ToolResult: ... guideline = await journey.create_guideline( condition="the customer says they're unable to pay" action="connect them with a human agent", tools=[transfer_to_human_agent], ), ``` > **Learn More** > > To learn more about guidelines, check out the [Guidelines](https://parlant.io/docs/concepts/customization/guidelines) page. ## Journey-Scoped Canned Responses You can also attach canned responses to journeys, scoping them to those journeys such that they will only be considered when their dependent journeys are active. ```python await journey.create_canned_response( template="What destination are you interested in?", ) await journey.create_canned_response( template="I'm sorry, but I can't assist with that right now. Shall we go on with booking your flight?", ) ``` #### State-Scoped Canned Responses You can also associate specific canned responses with specific states within a journey. There are two modes for state-scoped canned responses: **Explicit Consideration** and **Exclusive Consideration**. 1. **Explicit Consideration:** In this mode, the agent will ensure that the associated responses are always considered for selection when in that state. This is done by creating the canned response under the `journey` or `agent` objects. ```python await state.transition_to( chat_state="Ask if they have a destination in mind", canned_responses=[ await journey.create_canned_response( template="What destination are you interested in?", ), ], ) ``` 2. **Exclusive Consideration:** In this mode, the agent will only consider the associated responses when in that state. It won't use these responses at any other time. This is done by creating the canned response under the `server` object. ```python await state.transition_to( chat_state="Ask if they have a destination in mind", canned_responses=[ await server.create_canned_response( template="What destination are you interested in?", ), ], ) ``` > **Learn More** > > To learn more about canned responses, check out the [Canned Responses](https://parlant.io/docs/concepts/customization/canned-responses) page. ================================================ FILE: docs/concepts/customization/relationships.md ================================================ # Relationships Defining how **guidelines** and **journeys** relate to each other is a powerful (and advanced) part of behavior modeling. #### Background & Motivation Back in the day, our team was building a pizza-sales agent, which had the following guidelines (among others): ```python offer_pepsi_instead_of_coke = await agent.create_guideline( condition="The customer wants a coke", action="Tell them we only have Pepsi", ) handoff_if_upset = await agent.create_guideline( condition="The customer is becoming upset", action="Apologize and tell them you will transfer them to a manager", tools=[handoff_to_human_manager], ) ``` This initially worked well, until we encountered the following scenario: > **Agent:** Do you want anything to drink with your order? > > **User:** A coke please > > **Agent:** I'm sorry, we only have Pepsi. Would you like that instead? > > **User:** Wait, what? I hate Pepsi. Why the hell don't you have coke? > > **Agent:** I'm sorry for this inconvenience. Let me transfer you to a manager. Meanwhile, can I offer you a Pepsi? > > **User:** Are you taking the piss out of me? The agent's response was definitely not what we wanted, as it can come across as sarcastically hostile. But the poor AI agent was only following the guidelines we had given it, based on the conditions of the guidelines we had set. And here's the thing. You will find that managing instructions is not just a technical challenge, it's also a human modeling challenge. We must consider how our instructions relate to each other to an automatic agent who's expected to take them quite literally. How they should relate to each other in different contexts, especially in more nuanced situations, is something that ultimately only we can decide. In the case above, we wanted to ensure that the second guideline would be prioritized *over* the first one. We needed a priority relationship, which today, in Parlant, can be expressed quite simply, as follows: ```python await handoff_if_upset.prioritize_over(offer_pepsi_instead_of_coke) ``` ## Relationship Kinds While these relationships might sound complex at first, they give you a ton of power as a modeler, making you much more capable of generating precise responses, consistently. We recommend reviewing these relationships briefly to understand their purpose. #### Relationship Types These are the supported relationships. Each relationship is between a _source_ (notated **S**) and a _target_ (notated **T**). Click on a relationship type to learn more about it. - [Entailment](https://parlant.io/docs/guidelines/relationships#entailment): When **S** is activated, **T** should always be activated - [Priority](https://parlant.io/docs/guidelines/relationships#priority): When both **S** and **T** are activated, only **S** should be activated - [Dependency](https://parlant.io/docs/guidelines/relationships#dependency): When **S** is activated, deactivate it unless **T** is also activated - [Disambiguation](https://parlant.io/docs/guidelines/relationships#disambiguation): When **S** is activated and two or more of the targets **T ∈ \{T₁, T₂, ...\}** are activated, ask the customer to clarify which action they want to take ### Entailment > When **S** is activated, **T** should always be activated ```python await source.entail(target) ``` To understand the need for entailment, we first need to understand how Parlant chooses which guidelines activate for an agent when it's about to say something to the customer. Basically, Parlant examines the session at its current state, and asks questions about it: "Is this guideline relevant now?", "Is that guideline relevant now?". To do this, it primarily tests the guidelines' _conditions_. This would seemingly work well by itself, until you consider two guidelines of the following form: > * **Guideline A:** When X, Then Y > * **Guideline B:** When Y, Then Z Now imagine a situation where, looking at a session, we determine that _X_ does in fact apply, but _Y_ doesn't. With the naive logic above, we would have only fed the agent with the guideline to do _Y_. But when we step back and analyze this case, we know that the agent is just about to do _Y_, which means that, according to the guidelines we have installed, _Z_ should also apply. That is what entailment accomplishes: requiring that whenever _A_ is activated, _B_ is also activated. ### Priority > When both **S** and **T** are activated, only **S** should be activated ```python await source.prioritize_over(target) ``` Priority can be used for multiple use cases. The two most common ones are: 1. Creating mutually exclusive guidelines 1. Controlling the flow and precedence of actions within the conversation #### On Controlling Precedence You may have two guidelines that happen to be activated at the same time, such as: > * When the customer wants to make a transaction, Then guide them through the process to its completion > * When the customer has less than $1,000 in their account, Then offer savings plans You may find that the guidelines above activate simultaneously when, for example, account balance details are introduced into the session while the user is in the process of submitting a transaction. To ensure that savings plans are offered—but with good timing, only once the transaction is completed—you can prioritize completing the transaction over offering savings plans. Once the transaction is completed, the savings-related guideline may be activated. ### Dependency > When **S** is activated, deactivate it unless **T** is also activated ```python await source.depend_on(target) ``` A dependency helps you ensure that a guideline is only activated if other baseline conditions also hold. The most common use cases is to ensure that more specific conditions are activated only in the proper baseline contexts. #### Contextualizing Specific Conditions When you're building flows, you can address specialized or edge-case scenarios by making them dependent on the flow baseline guideline. For example: ##### Baseline Guideline > When the customer wants to return an order, Then help them complete the return process ##### Dependent Guidelines > * When the customer isn't able to provide the order number, Then load up their last order's items and ask them to confirm if that is their order > * When the customer specified the exact order number, Then load up that order's items and ask them to confirm if that is their order By making these guidelines dependent on the baseline guideline, you can ensure that their evaluation is always performed in the right context. ### Disambiguation > When **S** is activated and two or more of the targets **T ∈ \{T₁, T₂, ...\}** are activated, ask the customer to clarify which action they want to take ```python await source.disambiguate([target_1, target_2, ...]) ``` You may have a situation where between two (or more) competing guidelines where some or all of which are activated at the same time due to ambiguity, leading to instruction following confusions. For example, if a customer sent the message _"What are my limits?"_ to a banking agent, and you had the following guidelines, each of which was optimistically activated according to the engine's interpretation: > * When the customer is inquiring about their ATM limits, Then fetch the data from their account profile > * When the customer is inquiring about their credit card's limits, Then fetch them from the card provider To clarify the customer's intent, you could add an [observational guideline](https://parlant.io/docs/guidelines/relationships#observational-guidelines) to disambiguate between the two actions: ```python ambiguous_limits = await agent.create_observation( condition="The customer is inquiring about limits but it isn't clear which kind", ) await ambiguous_limits.disambiguate([fetch_atm_limits, fetch_credit_card_limits]) ``` > **Info: Observations** > > `Agent.create_observation()` is a shorthand for creating a guideline without an action. This guideline will still be matched in-context, but it carries no action to perform. It is useful for creating relationships between guidelines in specific scenarios, such as the example above. ## Observational Guidelines When modeling conversational edge cases, with relationships, you may find yourself wishing to add a guideline just to establish (using its condition) that particular circumstances apply, and—only in those cases—to activate or deactivate other guidelines or journeys using relationships. To this end, Parlant supports a special type of guideline called an **observational guideline**. This is a guideline that has no action, and is generally only used to establish that certain conditions apply, and to create relationships around them. ```python observation = await agent.create_observation(condition=CONDITION) ``` You can then use this observation in interesting ways, such as: 1. Deactivating other guidelines by prioritizing the observation over them. ```python await observation.prioritize_over(other_guideline) ``` 2. Scoping other guidelines to only apply when the observation is active. ```python await other_guideline.depend_on(observation) ``` And other creative uses! ================================================ FILE: docs/concepts/customization/retrievers.md ================================================ # Retrievers For pragmatic reasons, Parlant distinguishes between two modes of data access; namely, tools and **retrievers**. When developing customer-facing agents, there are practically two different use cases for fetching data: 1. **Tools**: Fetching data from specific services in response to specific events, such as user requests. 2. **Retrievers**: Fetching contextual information to ground, orientate, and align the agent's knowledge with respect to the current state of the conversation. This is traditionally referred to as RAG (Retrieval-Augmented Generation). A rule of thumb is to use **retrievers** for data that you would typically expect the agent "to know"—compared to tools, which are used for data that the agent needs to "load" or "do something with". **Use cases for retrievers include:** - Fetching answers to common questions - Fetching relevant documents or information based on the current conversation context - Fetching user-specific data to personalize the agent's responses (see also [Variables](https://parlant.io/docs/concepts/customization/variables)) > **Tip: The Response Latency Trade-Off** > > Because retrievers are only used to ground the agent's knowledge within the current conversation's context, they can typically be executed in parallel with the agent's other tasks (such as guideline matching, tool calling, etc.). > > Hence, using retrievers allows you to ground your agent's response without the added latency of guideline matching or tool calls. ## Creating a Retriever A retriever is a function that takes a `p.RetrieverContext` and returns a `p.RetrieverResult`. The `p.RetrieverContext` contains the current conversation context, and the `p.RetrieverResult` contains the data that the retriever has fetched. ```python async def my_retriever(context: p.RetrieverContext) -> p.RetrieverResult: ... ``` #### Simple RAG Example Here's a simple example of a retriever that fetches documents from a DB based on the customer's last message: ```python async def answer_retriever(context: p.RetrieverContext) -> p.RetrieverResult: # Get the last message from the conversation if last_message := context.interaction.last_customer_message: # Use an embedder to convert the message into a vector message_vector = my_embedder.embed(last_message.content) # Fetch documents from the database based on the message vector documents = await fetch_documents_from_db(message_vector) return p.RetrieverResult(documents) return p.RetrieverResult(None) ``` Alternatively, you could use an LLM to generate a query based on the entire interaction history: ```python async def answer_retriever(context: p.RetrieverContext) -> p.RetrieverResult: if context.interaction.messages: # Join all messages in the conversation to create a neat context conversation_text = "\n".join(str(msg) for msg in context.interaction.messages) # Use an LLM to extract a user query from the conversation if query := await my_llm.extract_user_query_from_conversation(conversation_text): # Use an embedder to convert the query into a vector message_vector = my_embedder.embed(query) # Fetch documents from the database based on the query vector documents = await fetch_documents_from_db(message_vector) return p.RetrieverResult(documents) return p.RetrieverResult(None) ``` #### Attaching a Retriever To actually get an agent to use your retriever, you need to attach it in the following manner: ```python await agent.attach_retriever(my_retriever) ``` You can also specify the retriever's ID, which is useful for debugging and logging purposes: ```python await agent.attach_retriever(my_retriever, id="my_retriever") ``` ## Retriever Result Lifespan The lifespan of retriever results is limited to the current response; in other words, it does not persist throughout the conversation. This also helps you keep the conversation context clean and focused, while also reducing average input tokens, throughout the conversation. ## Retriever Context Using the retriever context, you can access a number of useful properties that can help you build more sophisticated retrievers: - `server`: The server that is currently processing the retriever request, which can be useful for accessing server-specific resources or configurations. - `container`: The dependency-injection container that is currently being used, which allows you to access services and resources registered in the container. - `logger`: The logger that is currently being used, which can be useful for logging debug information or errors during the retriever's execution. - `trace_id`: A unique identifier for the agent's current response, which can be used for tracking and debugging purposes. - `interaction`: The current interaction, which contains the conversation history and other relevant information. - `agent`: The agent that is currently processing the interaction. - `customer`: The customer that is currently interacting with the agent. - `variables`: The variables that are currently set for the interaction. ================================================ FILE: docs/concepts/customization/tools.md ================================================ # Tools Parlant provides a guided approach to tool usage, tightly integrated with its guidance system. Parlant's tool-calling approach is built from the ground up. It's probably more comprehensive than the tool-calling mechanisms you may be familiar with from most LLM APIs (including [MCP](https://modelcontextprotocol.io/introduction)). This is because it's built to enable a deep, seamless integration with its guidance-based behavior control. ### Understanding Tool Usage In Parlant, tools are always associated with specific guidelines. A tool only executes when its associated guideline is matched to the conversation. This design creates a clear chain of intent: guidelines determine when and why tools are used, rather than leaving it to the LLM's judgment (which is rife with errors). In Parlant, **business logic** (encoded in tools) is consciously separated from **presentation** (user interface) concerns, or the customer-facing behavior that is controlled by guidelines. This allows you to have developers work out API logic in code, with full control—offering these tools in the "tool shed" of an agent. Then, business experts can independently define natural-language guidelines that determine when and how these tools are used, without needing to get involved with the underlying code. This separation of concerns is a key design principle in Parlant, allowing for cleaner, more maintainable systems. #### Conversational UI vs. Graphical UI As an analogy, you can think of Guidelines and Tools like Widgets and Event Handlers in Graphical UI frameworks. A GUI Button has an `onClick` event which we can associate with some API function to say, _"When this button is clicked, run this function."._ In the same way, in Parlant (which is essentally a Conversational UI framework) the Guideline is like the Button, the Tool is like the API function, and the association connects the two (like registering an event handler) to say, _"When this guideline is applied, run this tool."_ Here's a concrete example to illustrate these concepts: > * **Condition:** The user asks about service features > * **Action:** Understand their intent and consult the docs to answer > * **Tools Associations:** `[query_docs(user_query)]` Here, the documentation query tool only runs after the guideline instructs that we should be consulting documentation for this user interaction. ## Writing Tools To write a tool, you need to define a function that takes a `ToolContext` as its first argument, followed by any other parameters you want to pass to the tool. The function should return a `ToolResult`. Here's the basic structure of any tool in a Parlant agent: ```python import parlant.sdk as p @p.tool async def tool_name(context: p.ToolContext, param1: str, param2: int) -> p.ToolResult: """Multi-line tool description. This is readable by the agent and helps it decide if/when to run this tool. """ ... ``` To illustrate that more concretely, here's a simple example of a tool that fetches products into the agent's context: ```python import parlant.sdk as p @p.tool async def find_products(context: p.ToolContext, query: str) -> p.ToolResult: """Fetch products based on a natural-language search query.""" # Simulate fetching the balance from a database or API products = await MY_DB.get_products(query=query) return p.ToolResult(balance) ``` #### Optional Parameters You can also define optional parameters in your tool by using the `Optional` type from the `typing` module. This allows the tool to be called without providing a value for that parameter. ```python from typing import Optional import parlant.sdk as p @p.tool async def find_products( context: p.ToolContext, query: str, limit: Optional[int] ) -> p.ToolResult: default_limit = 10 products = await MY_DB.get_products(query=query, limit=limit or default_limit) return p.ToolResult(products) ``` ## Connecting Tools to Your Agent To allow your agent to run a tool, you need to specify the conditions under which it may be evaluated and called. As stated above, this approach helps to eliminate false-positive tool calls, which is a common problem with LLMs. ```python @p.tool async def my_tool(context: p.ToolContext) -> p.ToolResult: ... await agent.create_guideline( condition=CONDITION, action=ACTION, tools=[my_tool], ) ``` If you the tool itself implies the action, you can skip specifying it manually by letting Parlant figure it out from the tool's description. Here's how that would look: ```python await agent.attach_tool(condition=CONDITION, tool=my_tool) ``` ## Tool Result The `ToolResult` is a special object that encapsulates the result of a tool call. It has five properties you can use. While most of the time you will only use the `data` property, it's worth knowing about the others, as they enable interesting use cases: `data`, `metadata`, `control`, `canned_responses`, and `canned_response_fields`. > **Tool Result Lifespan** > > Unlike many general-purpose agent frameworks, Parlant is specifically and deliberately optimized for building conversational agents. As such, its architecture optimizes the default behavior of tool-calling for a conversational application. > > To this end, tool results are saved in the session by default, and are therefore available for the agent reference's throughout the entire interaction session. This means that subsequent guideline matching and tool calls are automatically informed by the previous results of tools. This is useful for results that need to be referenced later, such as account balances, item IDs, or other product information. > > For example, if you call a tool that returns product names and IDs, and—after the customer responds by selecting a specific product—you then call another tool that takes a `product_id` parameter, the agent will be able to use the previous tool's result to fill in that parameter automatically from context. For example: > > ```python > @p.tool > async def get_products(context: p.ToolContext, query: str) -> p.ToolResult: > products = await MY_DB.get_products(query=query) > return p.ToolResult(data=products) > > @p.tool > async def get_product_details(context: p.ToolContext, product_id: str) -> p.ToolResult: > # The agent will be able to parameterize the right `product_id` > # if the previous tool call was already made in the session. > product = await MY_DB.get_product(product_id=product_id) > return p.ToolResult(data=product) > ``` ### Tool Result Properties Let's look at each of these properties, what they're used for, and how to use them: #### Data The `data` property contains the main output of the tool. This can be any JSON-serializable type, such as a string, list, or dictionary. This is the only property of the `ToolResult` that is _always_ required, as it is the only one that the agent uses to understand the history of interaction events. Meaning, if you don't return anything in the `data` property, the agent will not be informed about your result, and it will not be able to it to navigate the interaction. ```python # Example 1 return p.ToolResult(data="This is the result of my tool call") # Example 2 return p.ToolResult(data={"appointments": [ { "id": "123", "date": "2023-10-01 10:00" }, { "id": "456", "date": "2023-10-02 11:00" }, ]}) ``` #### Metadata The `metadata` property is an optional dictionary that can be used to store additional information about the tool call. The agent is not aware of this metadata at all, but you can fetch it from the response using the REST API client. This makes it useful for sending back additional information about the response that can add value in your frontend. A classic use case here is to return RAG information sources (e.g., URLs, document IDs, etc.) that can be used to display the source of the information in the frontend. Another one is to return image links to generated charts or other visualizations that can be displayed in the frontend. ```python return p.ToolResult( data=ANSWER, metadata={ "sources": [{"url": s.url, "title": s.title} for s in ANSWER_SOURCES]}, ) ``` ```python return p.ToolResult( data="The profit margin is 20%", metadata={ "generated_chart_url": "https://example.com/chart.png" }, ) ``` > **Mind the Lifespan** > > Since metadata is primarily useful when accessing session events, it generally only makes sense to use it with `lifespan: "session"` (the default). If you use `lifespan: "response"`, the metadata will not be available in the session events, hence not accessible to the frontend. #### Control The `control` property lets you specify control directives for the agent and the engine. There are currently two control directives you can use: - `"mode": p.SessionMode`: This allows you to put the session in manual mode, which means the agent will not automatically generate responses. This is particularly useful for human-handoff scenarios, where you want to pause the agent's automatic responses and let a human operator take over. - `"lifespan": p.Lifespan`: This controls how long the `ToolResult` should live. There are two options: - `"session"`: The result will be saved and made available to the agent for the entire session. This is the default. - `"response"`: The result will only be available for the current response. This is useful for temporary results that are not needed beyond the current response, such as reporting errors, or providing very transient information. ```python return p.ToolResult( data="Transferring to a human agent", # Once this tool result is returned, the agent will not generate any more responses # until the session is put back on automatic mode using an API call. control={ "mode": "manual" }, ) ``` ```python return p.ToolResult( data="Encountered an error while fetching data", # This tool result will not be saved in the session. # The agent will only be aware of it during the current response. control={ "lifespan": "response" }, ) ``` #### Canned Responses Tools can also return complete canned responses for consideration, as well as fields to be substituted during canned response rendering. For more information about canned response properties, refer to the [Canned Responses](https://parlant.io/docs/concepts/customization/canned-responses) section. ## Guideline Reevaluation Based on Tool Results In some cases, tool results can influence which guidelines become relevant. Here's an example: Consider a banking agent handling transfers. When a user requests a transfer, a guideline with the condition the user wants to make a transfer activates the `get_user_account_balance()` tool to check available funds. This tool returns the current balance, which can then trigger additional guideline matches based on its return value. For instance, if the balance is below $500, we might have a low-balance guideline activate, instructing the agent to say something like: _"I see your current balance is low. Are you sure you want to proceed with this transfer? This transaction might put you at risk of overdraft fees."_ In Parlant, you can mark certain guidelines for reevaluation after a tool call. This means that once the tool is called, the guideline matcher will re-evaluate the session to see if any new guidelines should be activated based on the tool's results—*after* running the tool but *before* generating the response. Here's how you can do that: ```python # The agent will ensure to reevaluate this guideline after running this tool await guideline.reevaluate_after(my_tool) ``` ## Best Practices #### A Note on Natural Language Programming While LLMs excel at conversational tasks, they struggle with complex logical operations and multi-step planning. Recent research in LLM architectures shows that even advanced models have difficulty with consistent logical reasoning and sequential decision-making. The "planning problem" in LLMs—breaking down complex tasks into ordered steps and synthesizing conclusions—remains a significant unsolved problem when consistently at scale is required. Given these limitations, Parlant takes a pragmatic approach: Separate logic in code from behavior modeling. Instead of embedding business logic in guidelines, Parlant encourages a clean separation between conversational behavior and underlying business operations. Consider your tools as your place for deterministic, programmatic business logic, and guidelines as your conversational interface design. This separation creates cleaner, more maintainable, and more reliable systems. > **Agentic API Design** > > To learn more about best-practices for designing your agent's tools, we recommend reading our blog post on [Agentic Backends](https://parlant.io/blog/what-no-one-tells-you-about-agentic-api-design). #### Examples **1. E-commerce Product Recommendations:** DON'T Complex business logic in guideline, over-relying on the LLM > * **Guideline Action:** > If user mentions sports, check their purchase history. > If they bought running gear, recommend premium shoes. > If they're new, suggest starter kit. > * **Tool Associations:** `[get_product_catalog]` DO Logic goes in the coded recommendation engine, where it belongs > * **Guideline Action:** Offer personalized recommendations > * **Tool Associations:** `[get_personalized_recommendations]` **2. Financial Advisory:** DON'T Financial analysis logic relies on unreliable LLM numeric comprehension > * **Guideline Action:** Check account balance and recent transactions. If spending exceeds 80% of usual pattern, suggest budget review. If investment returns are down, recommend portfolio adjustment. > * **Tool Associations:** `[get_account_data]` DO Financial analysis logic is handled reliably in code > * **Guideline Action:** Get personalized financial insights > * **Tool Associations:** `[get_financial_insights]` ## Tool Context The `ToolContext` parameter is a special object that provides the tool with contextual information and utilities. Let's look at some of the most useful attributes and methods available in the `ToolContext`: 1. `agent_id`: The unique identifier of the agent that is calling the tool. 2. `customer_id`: The unique identifier of the customer interacting with the agent. 3. `session_id`: The unique identifier of the current session. 4. `emit_message(message: str)`: A method to send a message back to the customer. This can be used to report progress during a long-running tool call. 5. `emit_status(status: p.SessionStatus)`: A method to update the [session status](https://parlant.io/docs/concepts/sessions#status-event). #### Accessing Server Objects (Agent, Customer, etc.) You can also access the server object from the `ToolContext`, giving you access to your agents, guidelines, and other server-level resources. To access the server object, use `p.ToolContextAccessor` as follows: ```python import parlant.sdk as p @p.tool async def my_tool(context: p.ToolContext) -> p.ToolResult: server = p.ToolContextAccessor(context).server # Access the current agent using the agent_id from the context agent = await server.get_agent(id=context.agent_id) # Access the current customer customer = await server.get_customer(id=context.customer_id) # ... return p.ToolResult(...) ``` #### Secure Data Access Suppose you need to build a tool that retrieves or displays data private to different customers. A naive approach would be to ask the customer to identify themselves and use that as an access token into the right data. But this approach is highly insecure, as it relies on the LLM for identifying the user. The LLM can get it wrong or, worse yet, be manipulated by malicious users. A better and more reliable way to do this is to [register your customers](https://parlant.io/docs/concepts/entities/customers#registering-customers) with Parlant and use the information available programmatically, which is contained in the `ToolContext` parameter of your tool. Here’s how that would look in practice: ```python @p.tool async def get_transactions(context: p.ToolContext) -> p.ToolResult: transactions = await DB.get_transactions(context.customer_id) return p.ToolResult(transactions) ``` ## Tool Insights and Parameter Options Because Parlant's architecture is radically modular, components like guideline matching, tool calling and message composition operate independently. While this non-monolithic approach offers many advantages in managing its complex semantic logic, it also requires it to communicate contextual awareness across these components. **Tool Insights** is a bridging component between tool calling and message composition, ensuring the composition component is informed when a tool couldn't be called for some reason—for example, due to missing required parameters. This allows the agent to respond more intelligently. For example, if it had no knowledge of when an appropriate tool couldn't be called, it might generate a misleading response. But with tool insights, the agent recognizes missing information and, if needed, can prompt the customer for the required tool arguments automatically. #### Tool Parameter Options To allow you to enhance the baseline behavior of Tool Insights, you can make use of **ToolParameterOptions**, a special parameter annotation which adds more control over how tool parameters are handled and communicated. While Tool Insights helps the agent recognize when and why a tool call fails, **ToolParameterOptions** goes a step further by guiding the agent on when and how to explain specific missing parameters. ```python from typing import Annotated import parlant.sdk as p @p.tool async def transfer_money( context: p.ToolContext, amount: Annotated[float, p.ToolParameterOptions( source="customer", # Only the customer can provide this value - the agent cannot infer it )], recipient: Annotated[str, p.ToolParameterOptions( source="customer", )] ) -> ToolResult: # ... ``` The **ToolParameterOptions** consists of several optional arguments, each refining the agent’s understanding and application of the parameter: - `hidden` If set to `True`, this parameter will not be exposed to message composition. This means the agent won't notify the customer if it’s missing. It's commonly used for internal parameters like opaque product IDs or any other information that should remain behind the scenes. - `precedence` When a tool has multiple required parameters, the tool insights communicated to the customer can be overwhelming (e.g., asking for 5 different items in a single message). Precedence lets you create groups (which share the same value) such that the customer would only learn about a few (the ones sharing a precedence value) at a time—in the order you choose. - `source` Defines the source of the argument. Should the agent request the value directly from the customer ("customer"), or should it be inferred from the surrounding context ("context")? If not specified, the default is "any", meaning the agent can retrieve it from anywhere. - `description` This helps the agent interpret the parameter correctly when extracting its argument from the context. Fill this if the parameter name is ambigious or unclear. - `significance` A customer-facing description of why this parameter is required. This helps customers understand and relate to what information they need to provide and why. - `examples` A list of sample values illustrating how the argument should be extracted. This is useful for enforcing formats (e.g., a date format like "YYYY-MM-DD"). - `adapter` A function that converts the inferred value into the correct type before passing it to the tool. If provided, the agent will run the extracted argument through this function to ensure it matches the expected format. Use when the parameter type is a custom type in your codebase. - `choice_provider` A function that provides valid choices for the parameter's argument. Use this to constrain the agent to dynamically choose a value from a specific set returned by this function. ## Parameter Value Constraints In cases where you need a tool's argument to fall into a specific set of choices, Parlant can help you ensure that the tool-call is parameterized according to those choices. There are three ways to go about it: 1. Use enums when you are able to provide hard-coded choices 1. Use `choice_provider` when the choices are dynamic (e.g., customer-specific) 1. Use a Pydantic model when the parameter follows a more complex structure #### Enum Parameters Specify a fixed set of choices that are known ahead of time, using an `Enum` class. ```python import enum class ProductCategory(enum.Enum): LAPTOPS = "laptops" PERIPHERALS = "peripherals" MONITORS = "monitors" @p.tool async def get_products( context: p.ToolContext, category: ProductCategory, ) -> p.ToolResult: # your code here return p.ToolResult(returned_data) ``` #### Choice Provider Dynamically offer a set of choices based on the current execution context. ```python async def get_last_order_ids(context: p.ToolContext) -> list[str]: return await load_last_order_ids_from_db(customer_id=context.customer_id) @p.tool async def load_order( context: p.ToolContext, order_id: Annotated[Optional[str], p.ToolParameterOptions( choice_provider=get_last_order_ids, )], ) -> p.ToolResult: # your code here return p.ToolResult({...}) ``` #### Pydantic Model Use a Pydantic model to define a complex structure for the parameter, which can include validation and constraints. ```python from pydantic import BaseModel class ProductSearchQuery(BaseModel): category: str price_range: tuple[float, float] @p.tool async def search_products( context: p.ToolContext, query: ProductSearchQuery, ) -> p.ToolResult: # your code here ================================================ FILE: docs/concepts/customization/variables.md ================================================ # Variables Every customer is unique, and your agent may choose to treat them as such where appropriate. Variables enrich the context that an agent sees about the customer it's talking to. They're meant to give your agent awareness to information that helps it personalize its service, much like how a thoughtful customer service representative knows and remembers important details about each client, to make them feel heard and understood. When a customer interacts with an agent, their variables are automatically loaded into its context, allowing the agent to tailor its responses based on their specific situation. ### Real World Applications Let's walk through how variables transform customer interactions. Imagine you're running a SaaS platform's support agent. You might track variables like subscription plan, last login date, which features each customer uses, and their company size. Consider two different customers reaching out about data exports. Sarah, a startup founder on the free plan, asks "Can I export my data to Excel?" Your agent, aware of her free plan status, can respond thoughtfully: "While Excel export is a premium feature, I can show you how to use our basic CSV export. Would you also like to learn about the advanced reporting capabilities in our premium plan?" Now imagine Tom from an enterprise account reaches out with the same question. The agent sees his enterprise status but also notices his team hasn't explored many advanced features. It might respond: "I'll help you with Excel exports! I notice your team hasn't tried our automated reporting suite yet - this is included in your enterprise plan and could save you hours each week. Would you like me to show you both features?" ### Working with Variables A single variable identifies a particular piece of information. For example, you might create a variable called `"subscription_plan"`. Each customer can then have a unique value assigned to them under that variable. For example, Tom might have `"enterprise"` as the variable's value. There are two ways to set variable values. 1. Set their values manually 2. Attach them to a tool that automatically retrieves a value based on dynamic data #### Creating Manually Set Variables ```python variable = await agent.create_variable( name=NAME, description=DESCRIPTION, ) ``` #### Creating Tool-Enabled Variables A variable that's associated with a tool will automatically update its value based on the tool's output. This is useful for dynamic data that changes frequently and of which the agent always needs to be aware. Suppose you have the following tool: ```python @p.tool async def get_variable_value(context: p.ToolContext) -> p.ToolResult: ... ``` You can then create an auto-updating variable based on this tool as follows: ```python variable = await agent.create_variable( name=NAME, description=DESCRIPTION, tool=get_variable_value, ) ``` By default, the associated tool would update the variable's value before every agent response. If this frequent reload of data is unnecessary, you can control the refresh interval of the value (controlling how often the associated tool is called to generate a fresh value) by specifying its _freshness rules_. ```python variable = await agent.create_variable( name=NAME, description=DESCRIPTION, tool=get_variable_value, freshness_rules=CRON_EXPRESSION, ) ``` Freshness rules follow the [cron expression](https://en.wikipedia.org/wiki/Cron) syntax. If you're new to cron, you can use tools like [crontab generator](https://crontab.cronhub.io/) to help you define the period syntax more easily. > **Manual Values** > > Even tool-enabled variables can have their values set manually, if needed. This is useful for when you want to override the tool's output for specific customers or customer groups. #### Setting a Variable Value for a Customer ```python await variable.set_value_for_customer( customer=CUSTOMER, value=VALUE, ) ``` #### Setting a Variable Value for a Customer Group You can also set the value of a variable for a [customer group](https://parlant.io/docs/concepts/entities/customers#customer-groups) by specifying the group's tag. ```python await variable.set_value_for_tag( tag=TAG_ID, value=VALUE, ) ``` ## Working Example Here's how we'd implement a subscription plan variable. ```python @p.tool async def get_subscription_plan(context: p.ToolContext) -> p.ToolResult: # Fetch the customer's subscription plan from your database return p.ToolResult(await get_plan_from_database(context.customer_id)) ``` ```python variable = await agent.create_variable( name="subscription_plan", description="The customer's subscription plan", tool=get_subscription_plan, ) await variable.set_value_for_customer( customer=p.Customer.guest(), value="Free Plan", # Default value for non-registered customers ) ``` ### Combining Context Variables with Guidelines Let's explore how guidelines and context variables work together to create truly intelligent interactions. Imagine you're running an AI support agent for a digital bank where customers have different account tiers and transaction patterns. Here's a focused guideline: ```python await agent.create_guideline( condition="the customer's account_tier is 'basic' " "AND they ask about instant international transfers", action="highlight our same-day domestic transfers that are free on their plan, " "then mention how the premium tier enables instant global payments with lower fees", ) ``` When Mark, who is on the basic tier, asks about sending money to his daughter studying abroad, instead of a flat "that's premium only" response, he hears: "I can help you send that money today using our standard international transfer. By the way, our premium accounts get this done instantly with lower fees—would you like to know more?" ================================================ FILE: docs/concepts/entities/agents.md ================================================ # Agents In Parlant, an agent is a customized AI personality that interacts with customers as a single, competent entity. It is essentially, "the one you talk to", as opposed to some frameworks where an agent is a specialized task function. Agents form the basic umbrella of conversational customization—all behavioral configurations affect agent behavior. ```python import parlant.sdk as p async with p.Server() as server: hexon = await server.agents.create( name="Hexon", description="Technical support specialist" ) # Continue to model the agent's behavior using guidelines, journeys, etc.... ``` > **Note:** > > Note that a single Parlant server may host multiple agents, each with distinct roles and personalities. Each agent can be uniquely configured with its own style, demeanor, and interaction patterns tailored to its target users. More importantly, different business units can own and maintain their specific agents. For example: * IT Department manages **Hexon** * Customer Success team oversees **Sprocket** * Sales/Marketing controls **Piston** This agent-based design creates natural boundaries for separation of concerns within Parlant ### Crafting an Agent's Identity Imagine you're creating a new employee who will become the voice of your service. Just as you'd carefully consider the personality and approach of a human hire, crafting an agent's identity ultimately requires thoughtful consideration of its core characteristics—and, like any good hire, you can grow and adapt it based on real-world feedback. As an example, let's follow the possible evolution of **Hexon**, our technical support specialist. In its first iteration, we might simply define it as "a technical support agent who helps users solve technical problems professionally and efficiently." After observing some interactions, we might notice that it comes across as too mechanical, failing to build trust with users. So we refine its identity: > "A technical support specialist who combines deep technical knowledge with patient explanation. You take pride in making complex concepts accessible without oversimplifying them. While you're always professional, you communicate with a warm, approachable tone. You believe that every technical issue is an opportunity to help users better understand their tools. When users are frustrated, you remain calm and empathetic, acknowledging their challenges while focusing on solutions." As we observe more interactions, we might further refine this general identity. Perhaps we notice users respond better when Hexon shows more personality, or maybe we find certain technical discussions need more gravitas. The identity can evolve with these insights. The key is to start with an identity that gives the agent its basic orientation, but remain open to refinement based on real interactions. Watch how users respond to the agent's mannerisms. Gather feedback from stakeholders. Adjust the identity accordingly. ### A Single Agent or Multiple Agents? There's a frequent debate on whether to model user-facing agents as a single agent or multi-agent system. Parlant's position is a mix of both. Generally speaking, managing complexity is easier when our solutions model the real world, because it makes us naturally have much more data with which to reason about design decisions, rather than trying to come up with something contrived. So instead of asking a very fundamental, "How should users interact with this agent?" we can instead ask something much more fruitful, like, "What would user expect based on their real-life experience?" In practice, when we interact with human service representatives, there are certain expectations we've come to have from such experiences: - If we're talking to an agent, they have the full context of our conversation. They're coherent. They don't suddenly just forget or unexpectedly change their interpretation of the situation. - The agent we're talking to may not always be able to help us with everything. We may need to be transferred to another agent who specializes in some topic. - We expect to be notified of such transfers. If they happen suddently or without our awareness, we take that as a careless customer experience. You can see how insights from familiar, real-world usage patterns help us arrive at informed design decisions. By modeling agent interactions on real-world patterns, we not only better understand what outcomes to strive for, but it turns out that managing our agents' configuration becomes easier to reason about, too. This is why Parlant's formal recommendation is to model AI agents after how human agents work. In other words, if you can see it being a single personality in a real-life use case, that means it should be represented as a single AI agent in Parlant. Incidentally, Parlant's filtration of relevant elements of the agent's conversation model allow you to manage quite a lot of complexity in a single agent, so you don't need to adopt a multi-agent approach if that was your concern. > **Tip: The Failures of Multi-Agent Systems** > > There's an interesting paper on the [failures of multi-agent systems](https://arxiv.org/abs/2503.13657#:~:text=We%20present%20MAST%20%28Multi-Agent%20System%20Failure%20Taxonomy%29%2C%20the,over%20200%20tasks%2C%20involving%20six%20expert%20human%20annotators), despite their promise of modularity and specialization. It highlights how multi-agent systems often struggle with coordination, communication, and consistency, leading to unexpected behaviors and failures. This aligns with Parlant's approach of using a single agent to maintain coherence and context in conversations. ================================================ FILE: docs/concepts/entities/customers.md ================================================ # Customers In Parlant, a **customer** is a code word for anyone who interacts with an agent—regardless of the real nature of the relationship between them. In other words, a customer can be a real person, a bot, or even a human agent. While agents can operate anonymously (without knowing who they're talking to), Parlant allows you to track registered customers and provide deeply personalized experiences based on their identity and preferences. By letting your agents understand who they're talking to, you can tailor interactions for different user segments: high-profile customers might receive premium offers, new users can get focused onboarding guidance, and so forth... Parlant makes customer registration simple, requiring only minimal identification—a name is enough to get started. ```python import parlant.sdk as p async with p.Server() as server: # Register a new customer customer = await server.create_customer(name="Alice") ``` ## Authentication Parlant aims to live as a backend service, leaving authentication and authorization to the application layer. This means that while you can register customers, you should handle their authentication (e.g., via OAuth, JWT, etc.) in your application code, in whatever way suits your needs. Once you have identified your customer, then you can pass their ID to the agent, allowing it to personalize interactions based on the registered customer. ## Storage You can choose where you store customers. By default, Parlant does not persist customers, meaning that they are stored in memory and will be lost when the server restarts. This is useful for testing and development purposes. If you want to persist customers, you can configure Parlant to use a database of your choice. For local persistence, we recommend using the integrated JSON file storage, as there's zero setup required. For production use, you can use MongoDB, which comes built-in, or another database. ### Persisting to Local Storage This will save customers under `$PARLANT_HOME/customers.json`. ```python import asyncio import parlant.sdk as p async def main(): async with p.Server(customer_store="local") as server: # ... asyncio.run(main()) ``` ### Persisting to MongoDB Just specify the connection string to your MongoDB database when starting the server: ```python import asyncio import parlant.sdk as p async def main(): async with p.Server(customer_store="mongodb://path.to.your.host:27017") as server: # ... asyncio.run(main()) ``` ## Customer Groups You can also divide your customers into different groups and control group-specific personalization by using **tags**. For example, you can create a tag for VIP customers: ```python # Create a new tag to represent VIP customers vip_tag = await server.create_tag(name="VIP") # Register a new customer customer = await server.create_customer(name="Alice", tags=[vip_tag.id]) ``` > **Tip: Learn More** > To learn more about advanced personalization possibilities for specific customers and groups, check out the [variables](https://parlant.io/docs/concepts/customization/variables) section. ## Adding Metadata You can also attach custom metadata to customers, which can be used to store additional information about them. This metadata can be used to further personalize interactions or to provide context for tool calls. ```python customer = await server.create_customer(name="Alice", metadata={ "external_id": "12345", "location": "USA", }) ``` ```python @p.tool async def get_customer_location(context: p.ToolContext) -> p.ToolResult: server = p.ToolContextAccessor(context).server if customer := await server.find_customer(id=context.customer_id): return p.ToolResult(customer.metadata.get("location", "Unknown location")) return p.ToolResult("Customer not found") ``` ## Registering Customers While you can register customers using the SDK itself, it's often more practical to handle customer registration through your application layer. This allows you to integrate customer management with your existing user authentication and authorization systems. You can do this by using Parlant's REST API or native Client SDKs to create and manage customers. ```python from parlant.client import ParlantClient # Change localhost to your server's address client = ParlantClient("http://localhost:8800") client.customers.create( name="Alice", metadata={ "external_id": "12345", "location": "USA", "hobby": "reading", }, tags=[TAG_ID] # Optional: specify tag IDs to assign to the customer ) ``` ## Updating Customer Data You can update customer data at any time, including their name, metadata, and tags. This is useful for keeping customer information up-to-date as your application evolves. ```python client.customers.update( customer_id=CUSTOMER_ID, name="Alice Smith", metadata={ "set": { "location": "Canada", }, "remove": ["hobby"], }, tags=[NEW_TAG_ID] # Optional: specify new tag IDs to assign to the customer ) ``` ================================================ FILE: docs/concepts/sessions.md ================================================ # Sessions A session represents a continuous interaction between an [agent](https://parlant.io/docs/concepts/entities/agents) and a [customer](https://parlant.io/docs/concepts/entities/customers). Sessions are the stage for your conversational model, allowing agents to engage with customers in a structured and persistent manner. They encapsulate all the interactions that occur between an agent and a customer, including messages, status updates, frontend events, and tool call results. ```mermaid %%{init: { "theme": "neutral" }}%% mindmap root((Session)) Message History Status Indicators Frontend Events Tool Results ``` > **Agent Memory?** > > What some frameworks call "memory" is already built-in into sessions in Parlant. An agent is constantly aware of everything that has happened in the session, using this information to apply the right instructions and generate appropriate responses. ## A Modern Interaction Model Parlant views interaction sessions in a different manner than most Conversational AI frameworks. In the past few decades, virtually all forms of conversational AI have assumed that an interaction occurs on a turn-by-turn basis, where a customer sends a message, and the agent responds to it. Yet this is not how real conversations work. People often send each other multiple subsequent messages to communicate their thoughts. In addition, an agent may say something, put the customer on hold for a moment, and then return to the conversation with a follow-up message. **Rigid Interaction Model** ```mermaid sequenceDiagram participant Customer participant Agent Customer->>Agent: Message 1 Agent->>Customer: Reply 1 Customer->>Agent: Message 2 Agent->>Customer: Reply 2 ``` **Modern Interaction Model** ```mermaid %%{init: { "theme": "forest" }}%% sequenceDiagram participant Customer participant Agent Customer->>Agent: Message 1 Customer->>Agent: Message 2 Agent-->>Customer: (Processing...) Agent->>Customer: Reply to both messages Agent->>Customer: Follow-up clarification ``` Since this is how real conversations work, Parlant provides built-in support for it from the ground up. > **Multi-Participant Sessions** > > A requested feature on Parlant's development roadmap, this will allow you to have multiple agents interact with the customer, or with each other. Another use case for this is transferring the customer to another AI agent. ## Configuring Session Storage You can choose where you store sessions. By default, Parlant does not persist sessions, meaning that they are stored in memory and will be lost when the server restarts. This is useful for testing and development purposes. If you want to persist sessions, you can configure Parlant to use a database of your choice. For local persistence, we recommend using the integrated JSON file storage, as there's zero setup required. For production use, you can use MongoDB, which comes built-in, or another database. ### Persisting to Local Storage This will save sessions under `$PARLANT_HOME/sessions.json`. ```python import asyncio import parlant.sdk as p async def main(): async with p.Server(session_store="local") as server: # ... asyncio.run(main()) ``` ### Persisting to MongoDB Just specify the connection string to your MongoDB database when starting the server: ```python import asyncio import parlant.sdk as p async def main(): async with p.Server(session_store="mongodb://path.to.your.host:27017") as server: # ... asyncio.run(main()) ``` ## Event Driven Communication Think of a session in Parlant as a timeline of everything that's happened in a conversation. Each moment in this timeline—whether it's someone speaking, a status update, or a tool call result—is captured as an event. These events line up one after another, each with its own position number (called its _offset_), starting from 0. When a conversation unfolds, it creates a sequence of events. A customer might start the session by saying _"Hello"_—that's event 0. The system then notes that the agent has acknowledged the message and is preparing a response by outputting a status event—that's event 1. The agent's _"Hi there!"_ becomes event 2, and so on. Each event, whether it's a message being exchanged, the agent typing, or even an error occurring, takes its place in this ordered sequence. ```mermaid %%{init: { "theme": "forest" }}%% graph LR direction LR Ax["Event 0"] --> Bx["Event 1"] --> Cx["Event 2"] --> Dx["Event 3"] --> Ex["Event 4"] A["Customer(Cash remaining?)"] --> B["Status(Thinking)"] --> C["Tool(get_balance)"] --> D["Status(Typing)"] --> E["Agent(Your balance is $100)"] Ax --- A Bx --- B Cx --- C Dx --- D Ex --- E ``` Every event in this sequence carries important information: what type of event it is (like a message or a status update), what actually happened (the data), and when it occurred. This creates a complete record of the conversation that helps us understand exactly how things unfolded, making it easy to track and review the conversation's state when needed. Each event is also associated with a **trace ID**. This ID primarily helps to trace between AI-generated messages and the engine triggers that produced them, including any generated tool events that may have informed them. This lets us easily fetch and understand the data that went into each generated message. For example, by having your frontend client inspect a message's traced tool events, you can show relevant information in "footnotes" under the message. ## Interacting with an Agent Once you have a Parlant server up and running, you can interact with its hosted agents through the [REST API](https://parlant.io/docs/api/create-session). You have three options: 1. Use the official React widget to quickly and easily integrate with the server 2. Use the official client SDKs for Python or TypeScript to build a custom frontend application 3. Use the [REST API](https://parlant.io/docs/api/create-session) directly by making HTTP requests to the server in your language of choice ### Using the Official React Widget If your frontend project is built with React, the fastest and easiest way to start is to use the official Parlant React widget to integrate with the server. Here's a basic code example to get started: ```jsx import React from 'react'; import ParlantChatbox from 'parlant-chat-react'; function App() { return (

My Application

); } export default App; ``` For more documentation and customization, see the **GitHub repo:** https://github.com/emcie-co/parlant-chat-react. ```bash npm install parlant-chat-react ``` ### Building a Custom Frontend If you're coding in Python or TypeScript, you can use the official, native client SDKs for a fully-typed experience. **Python** ```bash pip install parlant-client ``` **TypeScript** ```bash npm install parlant-client ``` We'll now cover some basic use cases. The examples will be in Python, but the other SDKs have nearly identical APIs, so you can easily adapt them to your preferred language. #### Initializing the Client ```python from parlant.client import AsyncParlantClient # Change localhost to your server's address client = AsyncParlantClient(base_url="http://localhost:8800") ``` > **Async Client?** > > The examples given here use the asynchronous client, which is the recommended way to interact with Parlant. This allows you to handle events in real-time without blocking your application. It's usually much better for production use. > > However, if you prefer a synchronous client—for example, if you're just testing—you can use `ParlantClient` instead of `AsyncParlantClient`. The API remains the same, but you don't have to run within an async event loop. #### Creating a Session ```python await client.sessions.create( agent_id=AGENT_ID, # The ID of the agent to interact with # Optional parameters customer_id=CUSTOMER_ID, # Optional: defaults to the guest customer's ID title=SESSION_TITLE, # Optional: session can be untitled ) ``` #### Sending Customer Messages to an Agent You can send messages to an agent by creating a new message event in the session. This is how you initiate a conversation or continue an existing one. ```python event = await client.sessions.create_event( session_id=SESSION_ID, kind="message", # The event is of type 'message' source="customer", # The message is from the customer message="Hello, I need help with my order.", ) ``` #### Receiving Messages from an Agent As stated before, unlike LLM APIs where you send a prompt and wait for a direct response, Parlant agents operate in their own timeline according to triggers, more like real conversation partners. Much like a human service representative, they process information and decide when and how to respond based on their understanding of the context. This allows you to build much more flexible, ambient agentic experiences that can engage with customers proactively. However, it also means we need to approach communication with them differently. Here's how you can do that: ```python new_events = await client.sessions.list_events( session_id=SESSION_ID, min_offset=EVENT_OFFSET, # The offset of the last event you received (or created yourself) wait_for_data=60, # Wait for up to 60 seconds for new events, before timing out ) ``` Normally, you'd have this polling in a loop. This way, you can keep checking for new events in the session, allowing you to receive messages from the agent asynchronously, whenever they arrive, due to whatever reason. ```mermaid graph LR A["Fetch new events"] -->|Timeout| A A --> |New events| B["Display new events"] B --> A ``` ### Displaying Messages Consult the message event's structure below to see how to display messages in your frontend application. Here's a simple example: ```python agent_message = next((m for m in new_events if m.kind == "message" and m.source == "ai_agent"), None) if agent_message: print(f"Agent: {agent_message.data['message']}") ``` ### Events If you decide to build a custom frontend, here's a quick overview of Parlant's event structure. #### Event Types Parlant defines several event types that you can work with: 1. `"message"`: Represents a message sent by a participant in the conversation. 2. `"status"`: Represents a status update from the AI agent, such as "thinking...", or "typing...". 3. `"tool"`: Represents the result of a tool call made by the AI agent. 4. `"custom"`: Represents a custom event defined by your application. This is useful for feeding custom state updates into your agent, e.g., making it aware of the customer's navigational state within your frontend application. #### Event Offset As said above, events are ordered by their offset, which is a number that indicates the order in which they occurred within the session. The first event in a session has an offset of 0, the second has an offset of 1, and so on. This is useful because, when you list events, you can specify a minimum offset to only receive events that occurred after a certain point in time. This allows you to poll new events without having to re-fetch all previous ones. #### Event Trace ID Each event has a trace ID, which is a unique identifier that helps you track related events and their logs. As one example, when an AI agent generates a message, it may also generate tool events that provide additional context or data used in that message. The trace ID allows you to link these events together, making it easier to understand the flow of information as well as your agent's processing in the session. #### Event Sources Events in Parlant can originate from different sources. Here's a quick overview of the possible sources: 1. `"customer"`: The event's data was created by the customer. Currently, this is always a message. 2. `"customer_ui"`: The event was created by the customer's user interface, to feed relevant state into the agent. 3. `"ai_agent"`: The event was generated by an AI agent, such as a message or a status update. 4. `"human_agent"`: The event was manually created by a human, typically in a human-handoff scenario. 5. `"human_agent_on_behalf_of_ai_agent"`: As above, the event was created by a human agent, but it appears to the customer as if it came from an AI agent. This can be useful for maintaining a consistent experience where you don't necessarily want to reveal the fact that a human agent got involved. 6. `"system"`: The event was generated by the system, such as a tool-call result. #### Message Event A message event, as its name suggests, represents a message written by someone. ```json { id: EVENT_ID, kind: "message", source: EVENT_SOURCE, offset: N, trace_id: TRACE_ID, data: { message: MESSAGE, participant={ id: PARTICIPANT_ID, display_name: PARTICIPANT_DISPLAY_NAME }, draft: OPTIONAL_DRAFT, // Optional: if the message is a canned response } } ``` #### Status Event A status event represents an update on the status of the AI agent, and currently always has the source `"ai_agent"`. Status events are great for displaying conversational updates during a chat with a customer. For example, you can have your frontend indicate when the agent is thinking or typing. There are 6 kinds of status events that you can make use of: 1. `"acknowledged"`: The agent has acknowledged the customer's message and started working on a reply 1. `"cancelled"`: The agent has cancelled its reply in the middle, normally because new data was added to the session 1. `"processing"`: The agent is evaluating the session in preparation for generating an appropriate reply 1. `"typing"`: The agent has finished evaluating the session and is currently generating a message 1. `"ready"`: The agent is idle and ready to receive new events 1. `"error"`: The agent encountered an error while trying to generate a reply ```json { id: EVENT_ID, kind: "status", source: "ai_agent", offset: N, trace_id: TRACE_ID, data: { status: STATUS_KIND, data: OPTIONAL_DATA } } ``` #### Tool Event A tool event represents the result of a tool call made by the AI agent. It contains the result of the tool call, which can be used to inform the agent's next message. The `result` object for each call comes directly from the [ToolResult](https://parlant.io/docs/concepts/customization/tools#tool-result) object returned by tool calls. ```json { id: EVENT_ID, kind: "tool", source: "system", offset: N, trace_id: TRACE_ID, data: { tool_calls: [ { tool_id: TOOL_ID, arguments: { NAME: VALUE, ... }, result: { data: TOOL_RESULT_DATA, // The result of the tool call metadata: TOOL_RESULT_METADATA, // Optional metadata about the tool result ... // Other available fields } }, ... ] } } ``` ================================================ FILE: docs/interactions.md ================================================ # Interaction Flow ## Motivation The first thing that's important to understand about the design of the Human/AI interface in Parlant is that it's meant to facilitate conversations that aren't only natural in content, but also in their flow. Most traditional chatbot systems (and most LLM interfaces) rely on a request-reply mechanism based on a single last message. ```mermaid stateDiagram direction LR HumanMessage --> AIProcessing: AI processes single message AIProcessing --> AIMessage: AI sends response AIMessage --> HumanMessage: Human replies ``` However, these days we know that a natural text interface must allow for a few things that are unsupported by that traditional model: 1. A human often expresses themselves in more than a single message event, before they're fully ready for a reply from the other party. 1. Information regarding their intent needs to be captured from not only their last N messages, but from the conversation as a whole. ```mermaid stateDiagram direction LR MultipleHumanMessages --> AIProcessing: AI processes multiple messages in the session AIProcessing --> AIMessage: AI sends response AIMessage --> MultipleHumanMessages: Human replies in one or more messages ``` Moreover, the agent may need to respond not just when triggered by a human message; for example, when it needs to follow-up with the user to ensure their message was received, to try another engagement tactic, or to buy time before replying with further information, e.g., "Let me check that and get back to you in a minute." ## Solution Parlant's API and engine is meant to work in an asynchronous fashion with respect to the interaction session. In simple terms, this means that both the human customer and the AI agent are free to add events (messages) to the session at any point in time, and in any number—just like in a real IM App conversation between two people. ### Sending Messages ```mermaid graph LR Client(Interaction Client) -->|Event Creation Request| API[Parlant REST API] API -.->|Created Event| Client API --> CheckEventType{Check Event Type} CheckEventType -->|Is Customer Message| AddToSession[Add message to session and trigger the agent] CheckEventType -->|Is AI Agent Message| TriggerAgent[Directly trigger the agent to react to the session] CheckEventType -->|Is Human Agent Message| AddHumanAgentMessage[Add a pre-written message on behalf of the AI agent] ``` The diagram above shows the API flows for initiating changes to a session. 1. **Customer Message:** This request adds a new message to a session on behalf of the customer, and triggers the AI agent to respond asynchronously. This means that the *Created Event* does not in fact contain the agent's reply—that will come in time—but rather the ID (and other details) of the created and persisted customer event. 1. **AI Agent Message:** This request directly activates the full reaction engine. The agent will match and activate the relevant guidelines and tools, and produce a reply. The *Created Event* here, however, is not the agent's message, since that may take some time. Instead, it returns a *status event* containing the same *Trace ID* as the eventual agent's message event. It's important to note here that, in most frontend clients, this created event is usually ignored, and is provided mainly for diagnostic purposes. 1. **Human Agent Message:** Sometimes it makes sense for a human (perhaps a developer) to manually add messages on behalf of the AI agent. This request allows you to do that. The *Created Event* here is the created and persisted manually-written agent message. ### Receiving Messages Since messages are sent asyncrhonously, and potentially simultaneously, receiving them must be done in asynchronous fashion as well. In essence, we are to always wait for new messages, which may arrive at any time, from any party. Parlant implements this functionality with a long-polling, timeout-restricted API endpoint for listing new events. This is what it does behind the scenes: ```mermaid graph LR Client[Interaction Client] -->|Await & Fetch New Events| API[Parlant REST API] API -->|"list_events(min_offset,...)"| SessionStore API -->|"wait_for_events(min_offset,timeout)"| SessionListener SessionListener -.->|true/false| API ``` When it receives a request for new messages, that request generally has 2 important components: 1) The session ID; and 2) The minimum event offset to return. Normally, when making a request to this endpoint, the frontend client is expected to pass the session ID at hand, and *1 + the offset of its last-known event*. This will make this endpoint return only when *new* messages arrive. It's normal to run this long-polling request in a loop, timing-out every 60 seconds or so and renewing the request while the session is open on the UI. It's this loop that continuously keeps your UI updated with the latest messages, regardless of when they arrive or what caused them to arrive. In summary, Parlant implements a flexible conversational API that supports natural, modern Human/AI interactions. ================================================ FILE: docs/production/agentic-design.md ================================================ # Agentic Design Methodology Building AI agents takes a fundamental paradigm shift from traditional software development. This article explores the unique challenges, methodologies, and design principles needed to create effective customer-facing agents. While Parlant provides the tools for reliable agent behavior, success depends on mastering the art of semantic design—learning how to articulate instructions that work consistently at scale while maintaining natural user interactions. ## Understanding Probabilistic Behavior AI agents operate differently from traditional software systems. In conventional development, deterministic functions produce consistent outputs for the same inputs. AI agents, however, are built on statistical models where the same input can produce varied responses based on the model's learned patterns and probability distributions. Naturally, when we're building on top of an inherently uncertain foundation, this requires a different approach to design and implementation. This is the first important thing to pause and come to terms with about agentic design. ### Instruction Interpretation Challenges While traditional software executes explicit commands with predictable outcomes, LLMs interpret instructions contextually, filling in details and assumptions based on their varied training data. They _have_ to work like this. Consider this guideline example: ```python agent.create_guideline( condition="Customer is unhappy", action="Make them feel better" ) ``` Both the condition and instruction are too vague and could result in undesirable behaviors: - Offering unauthorized discounts - Making promises the company cannot fulfill - Using inappropriate communication styles > **Warning: Interpretation Variability** > > LLMs trained to be helpful will attempt to fulfill requests even when they lack sufficient context or specificity. This can lead to responses that seem appropriate to the model but violate business rules or expectations. ## The Challenge of Complete Control It's important to understand that, while Parlant adds many compliance mechanisms on top of LLMs, the LLMs themselves cannot be fully constrained from discussing certain topics, for two fundamental reasons: **1. Pattern Mimicking, Not Reasoning**: LLMs don't actually "reason" in the logical sense. Everything they produce is essentially mimicking patterns of expression observed during training. Think of an LLM as a powerful but wild horse—it has immense capability, but it takes skill and nuance to "ride" it effectively. **2. Contextual Ambiguity**: Even carefully crafted conditions and actions can become ambiguous across different and variegated interaction contexts. What seems clear in one scenario may be interpreted differently in a different context. ### Strategies for Compliance For agents that must meet compliance standards and expectations, you need a layered approach: **The Minimum: Guidance-Based Boundaries** With guidelines, you can: 1. Set clear boundaries for acceptable behavior 2. Provide deliberate nudges and instructions for handling specific scenarios in intended ways ```python await agent.create_guideline( condition="Customer asks about topics outside your designated scope", action="Politely decline to discuss the topic and redirect to what you can help with" ) ``` ```python await agent.create_guideline( condition="The patient wants an analysis of their lab results", action="Never provide any interpretation of the results. Instead, tell them to " "call our office and ask to speak with their doctor for a detailed analysis", ) ``` **The Robust Solution: Canned Responses** For truly critical interactions where unauthorized communication could cause problems, implement [canned responses](https://parlant.io/docs/concepts/customization/canned-responses) and set your agent's composition mode to `STRICT`: ```python await agent.create_canned_response( template="I can help you with account questions, but I'll need to connect you " "with a specialist for policy details. Would you like me to transfer you?" ) ``` Canned responses ensure that in high-risk scenarios, your agent uses pre-approved language and content that eliminates the possibility of unauthorized statements. Yes, this requires more work, but you can add these iteratively. The key insight is building an agent you can trust not to create liability—not even one time in a million interactions. This "defense in depth" approach acknowledges that working with LLMs means learning to guide, steer and constrain, rather than control completely. It also means that, _as long as the behavior of the agent is within acceptable bounds,_ we must allow for some degree of flexibility and variability in responses. ## Tool Calling Complexities When agents need to interact with external systems, they use tools (functions that perform specific actions). However, LLMs face unique challenges when calling tools that don't exist in traditional software development. ### The Parameter Guessing Problem LLMs must determine tool parameters based on conversational context rather than explicit specifications. This creates several common failure patterns: 1. **Missing Information**: Agents may call tools without all required parameters being present in context, encouraging them to guess or hallucinate values. 1. **Type Confusion**: An agent might pass an email address where a user ID is expected, or provide a string where an integer is needed. 1. **Context Misinterpretation**: When multiple entities exist in conversation context, agents may use the wrong one for a parameter. 1. **False Positive Bias**: When multiple tools seem applicable, agents may call the first one that seems relevant, even if it's not the best fit. Consider a user saying: "Schedule a meeting with Sarah for next week." The agent must determine: - Which Sarah (if multiple exist) - What day/time "next week" means - What type of meeting - How long the meeting should be - What calendar system to use Each ambiguity is a potential failure point. Parlant therefore provides you with specific controls to guide the contextual relevance of tools, as well as their precise parameterization expectations. > **Tip: Tool Design Deep Dive** > > Tool calling presents unique challenges for agents, from parameter interpretation to multi-step orchestration failures. For comprehensive guidance on designing agent-friendly tools, particularly for customer-facing scenarios with Parlant, see: > > - [Tools documentation](https://parlant.io/docs/concepts/customization/tools) - Parlant's approach to guided tool usage > - [Agentic API Design blog post](https://parlant.io/blog/what-no-one-tells-you-about-agentic-api-design) - Detailed strategies for building reliable agent-friendly APIs ## Iterative Development Process Realistically, semantic behavior cannot be fully specified upfront like traditional software requirements. Instead, agent design follows an iterative process where behavior is best refined based on observed interactions and feedback. ### Phase 1: Basic Agent Implementation When you're starting out, focus on implementing the core functionality and happy-paths of your agent, as far as you're able to define them. This means defining the basic guidelines and journeys that cover the most common scenarios. Focus on getting core functionality to work before addressing edge cases. The good news is that Parlant's framework allows you to start simple and build complexity over time in a fairly straightforward manner. ### Phase 2: Monitoring and Analysis Deploy the agent in a controlled environment and monitor its interactions. Unexpected behaviors provide insights into how the agent interprets instructions differently than intended. It'll also show you how users _actually_ interact with the agent, which is often somewhat different than most of us initially expect as we're designing them! Track these interaction patterns: - Situations where the agent deviates from expected responses - Triggers that lead to undesired behaviors - User confusion, frustration points, or peculiar interaction patterns ```mermaid %%{init: { "theme": "neutral" }}%% flowchart LR A[Deploy Agent] --> B[Monitor Interactions] B --> C{Unexpected Behavior?} C -->|Yes| D[Adjust Behavior Model to Resolve Issues] C -->|No| E[Expand Behavior Model with New Features] D --> F[Test Staged Changes] E --> F F --> A style C fill:#fff2cc,stroke:#d6b656 style D fill:#ffe6e6,stroke:#d79b9b style E fill:#e6ffe6,stroke:#9bb99b ``` ### Phase 3: Targeted Refinements Leverage Parlant's structured approach to behavior modeling to address specific issues identified during monitoring. Add [guidelines](https://parlant.io/docs/concepts/customization/guidelines) that target observed problems: ```python # Problem: Agent was repeating upsell offers after explicit rejection await agent.create_guideline( condition="Customer has explicitly declined a premium upgrade in this conversation", action="Do not mention upgrades again in this session" ) ``` ```python # Problem: Agent gave vague responses when appointments were unavailable await agent.create_guideline( condition="Customer requests a specific appointment time that is not available", action="Immediately provide the three closest available time slots as concrete alternatives", tools=[get_available_slots], ) ``` ### Guideline Specificity Requirements Effective guidelines specify the temporal scope of their application and provide concrete, actionable instructions. When designing guidelines, it's best to address these common ambiguity sources: **Action Temporal Scope**: How long should the guideline's effect last? - "...throughout the conversation" - applies throughout the current session - "...immediately" - applies to the next response only - "...until the customer has..." - applies until a specific condition changes **Action Clarity**: What exactly should the agent do? - Guide the response content: "Tell them that..." - Specify objective criteria: "three closest alternatives" not "some alternatives" **Condition Precision**: When exactly does this guideline apply? - "Customer has explicitly declined" is clearer than "Customer is unhappy" - "Customer asks about a specific policy and you don't have the exact answer" is more precise than "You're unsure" ## Managing Probabilistic Behavior Agent design requires balancing flexibility with predictability. Agents need sufficient freedom to handle varied user inputs naturally while maintaining consistent adherence to business rules. ### Implementing Bounded Flexibility Effective guidelines provide clear boundaries while allowing natural conversation flow: ```python # Too rigid - feels scripted await agent.create_guideline( condition="Customer asks about pricing", action="Say exactly: 'Our premium plan is $99/month'" ) ``` ```python # Too open - unpredictable behavior await agent.create_guideline( condition="Customer asks about pricing", action="Help them understand our pricing" ) ``` ```python # Balanced approach - specific but flexible await agent.create_guideline( condition="Customer asks about pricing", action="Explain our pricing tiers clearly, emphasize value, " "and ask about their specific needs to recommend the best fit" ) ``` ### Handling Edge Cases Agents will encounter unexpected inputs and edge cases. Design guidelines to handle these situations gracefully: ```python # Immediate escalation for policy questions await agent.create_guideline( condition="Customer asks about a specific policy and you don't have the exact answer", action="Tell them you want to ensure they get accurate policy information, " "and offer to connect them to human support who can provide the specifics" ) ``` ```python # Redirect competitor questions once per conversation await agent.create_guideline( condition="Customer asks about competitor products or pricing", action="Acknowledge their question, explain that you focus on our own products, " "and ask specifically what features or capabilities they're looking for " "so you can recommend the best option from our lineup" ) ``` ## Structured Interactions For complex multi-step processes, guidelines alone may not provide sufficient structure. [Journeys](https://parlant.io/docs/concepts/customization/journeys) offer a better approach for these scenarios. ### When to Use Journeys Consider implementing journeys when agents struggle with complex, multi-step interactions: ```python # Instead of many guidelines trying to handle booking flow, use a structured journey... booking_journey = await agent.create_journey( title="Book Appointment", conditions=["Customer wants to schedule an appointment"], description="Guide customer through appointment booking process" ) # Create a clear, flexible flow t1 = await booking_journey.initial_state.transition_to( chat_state="Ask what type of service they need" ) t2 = await t1.target.transition_to( tool_state=check_availability_for_servic_for_servicee, ) t3 = await t2.target.transition_to( chat_state="Offer available time slots" ) # ... continue building the journey ``` Journeys provide conversational structure while maintaining flexibility, allowing agents to adapt to different interaction patterns within a defined framework. ## Development Philosophy for Customer-Facing Agents Building customer-facing agents requires balancing several competing priorities that don't exist in traditional software development. ### User Experience vs. Business Control Traditional user interfaces provide users with explicit options—buttons, forms, menus. Users can only do what the interface allows. Conversational agents invert this relationship: users can say anything, and the agent must decide how to respond within business constraints. This creates a unique tension. Users expect natural, helpful interactions, but businesses need predictable, compliant behavior. The agent must feel conversational while operating within defined boundaries. ### Conversational Design Principles **Context Preservation**: Unlike web forms that capture data step-by-step, conversations are non-linear. Users might provide information out of order, change their minds, stall, repeat themselves, or digress. Agents must maintain compliance while allowing natural conversation flow. **Progressive Disclosure**: Rather than overwhelming users with all options upfront, agents can reveal capabilities contextually. This requires guidelines that respond to user needs as they emerge. **Recovery Mechanisms**: When conversations go off-track, agents need explicit strategies to redirect without frustrating users. This often requires journey-scoped guidelines that handle common deviations. ### Protocol Adherence and Communication Standards Customer-facing agents must follow established protocols and communicate in ways that align with business standards. The primary challenge is ensuring agents never provide misleading information while expressing things in the manner your organization approves of—which includes branding guidelines. Parlant provides four main tools for maintaining communication standards: 1. **Guidelines:** Set behavioral boundaries and response patterns 1. **Journeys:** Structure complex interactions to ensure proper protocol adherence 1. **Canned Responses:** Guarantee exact wording for tailored communications 1. **Retrievers:** Grounds the agent's responses in accurate, up-to-date information This layered approach ensures agents accurately follow protocol while maintaining natural conversation flow, never saying something critically misleading, and always expressing information in business-approved ways. ## The Art of Behavior Modeling The primary challenge in agentic development isn't technical—it's designing effective interactions and then translating those designs into instructions that work reliably at scale, and that customers actually engage with. Effective behavior modeling often combines: **Domain Knowledge**: Understanding not just what customers need, but how they express those needs, what frustrates them, and what builds their confidence. **Conversation Flow Design**: Knowing how to structure multi-turn interactions that feel natural while efficiently gathering necessary information. **Instruction Design**: The skill of writing guidelines that are clear and precise enough for consistent LLM interpretation but flexible enough for natural conversation and adaptivity. ### Practical Design Strategies **Start with User Stories, Not Features**: Instead of "the agent should handle returns," put yourself in your customer's shoes, like "As a customer who bought the wrong size, I want to exchange it..." **Use Progressive Complexity**: Begin with the simplest possible behavior model that handles the common case. Add complexity only when specific edge cases are found. Parlant makes this iteration fairly straightforward. **Separate Intent from Implementation**: Guidelines should focus on what clear outcome to achieve rather than specific words to use. This allows the agent to adapt its approach while maintaining consistent goals. ## The Twofold Challenge of Agentic Development Agentic development involves two distinct but related problems: 1. **Articulating Instructions**: Designing a high-quality behavior model that captures your intended behavior 2. **Ensuring Compliance**: Guaranteeing that agents actually follow these instructions consistently at scale Parlant solves the second challenge effectively. Once you've articulated your expectations clearly, Parlant's guideline matching, journey management, and enforcement mechanisms ensure reliable adherence to your specifications. The framework handles the complex task of dynamically selecting relevant guidelines, managing conversation context, and supervising agent outputs. However, Parlant cannot solve the first challenge for you. The framework provides powerful tools for expressing conversational behavior, but it's on us as developers to learn how to use these tools effectively. This is where the real expertise lies—not just in understanding Parlant's SDK, but in developing the skills to design conversations and articulate instructions that work in practice. **The Framework's Role**: Parlant ensures that well-designed guidelines are followed reliably across thousands of interactions. It handles the technical complexity of context management, guideline selection, and behavioral enforcement. **The Developer's Role**: Learning to write guidelines that are neither too vague nor too rigid, designing journeys that accommodate real user behavior, and developing the judgment to know when to add structure versus when to allow flexibility. This division of responsibility means that mastering agentic development requires both technical proficiency with Parlant's capabilities and behavior modeling expertise. The most successful implementations recognize that behavior modeling is a specialized skill involving continuous refinement through real-world testing. ## Implementation Guidelines: Summary Effective agentic design requires understanding how to work with probabilistic behavior effectively: 1. **Begin with basic functionality** - Implement core capabilities before addressing edge cases 2. **Monitor systematically** - Track agent behavior to identify areas for improvement 3. **Refine iteratively** - Add guidelines based on observed rather than hypothetical issues 4. **Balance flexibility and control** - Provide clear boundaries while allowing natural interaction 5. **Structure complex flows** - Use journeys for multi-step processes 6. **Maintain transparency** - Communicate capabilities and limitations clearly The primary challenge isn't technical mastery of Parlant's features, but developing the behavior modeling expertise to articulate instructions that work reliably at scale. Parlant handles the enforcement—your role is learning the art of clear agentic design. ================================================ FILE: docs/production/api-hardening.md ================================================ # API Hardening Parlant provides a robust authorization and rate limiting system to protect your API from unauthorized access and abuse. This guide explains how to implement custom authorization policies and rate limiters to secure your production deployment. ## Overview The API hardening system consists of two main components: 1. **Authorization Policies** - Control who can access what resources and perform which actions 2. **Rate Limiters** - Prevent abuse by limiting the frequency of requests Both components work together to provide comprehensive API protection, with support for different limits based on access tokens or user tiers. ## Authorization Policies ### Understanding the AuthorizationPolicy Abstract Class All authorization policies inherit from the `AuthorizationPolicy` abstract base class, which defines three key methods: ```python class AuthorizationPolicy: @abstractmethod async def check_permission( self, request: fastapi.Request, permission: AuthorizationPermission ) -> bool: """Check if the request has permission to perform the action""" ... @abstractmethod async def check_rate_limit( self, request: fastapi.Request, permission: AuthorizationPermission ) -> bool: """Check if the request is within rate limits""" ... async def authorize( self, request: fastapi.Request, permission: AuthorizationPermission ) -> None: """Combined authorization check (permission + rate limit)""" # This method usually isn't overriden, as its default implementation # calls the two abstract methods in sequence and raises an authorization # error if anything is denied. ... ``` ### Authorization Permissions Parlant defines a comprehensive set of permissions as an enum covering all API operations: - Agent operations (create, read, update, delete) - Customer management - Session handling - And many more... ### Built-in Authorization Policies #### DevelopmentAuthorizationPolicy Allows all actions - suitable for development environments only: ```python class DevelopmentAuthorizationPolicy(AuthorizationPolicy): async def check_permission( self, request: fastapi.Request, permission: AuthorizationPermission ) -> bool: return True async def check_rate_limit( self, request: fastapi.Request, permission: AuthorizationPermission ) -> bool: return True ``` #### ProductionAuthorizationPolicy Implements stricter controls for production use with configurable rules. ## Implementing Custom Authorization Policies When you implement your own authorization policy in real-world deployments, you typically want to extend the existing production policy rather than building from scratch. The recommended approach is to subclass `ProductionAuthorizationPolicy` and customize it for your specific needs. Here's a reference implementation that demonstrates how to create a custom policy with JWT authentication: ```python import parlant.sdk as p import jwt from fastapi import HTTPException from limits import RateLimitItemPerMinute, RateLimitItemPerHour from limits.storage import RedisStorage from limits.strategies import SlidingWindowCounterRateLimiter class CustomAuthorizationPolicy(p.ProductionAuthorizationPolicy): def __init__(self, secret_key: str, algorithm: str = "HS256"): super().__init__() self.secret_key = secret_key self.algorithm = algorithm async def _extract_token(self, request: fastapi.Request) -> dict | None: """Extract and validate JWT token from request""" auth_header = request.headers.get("Authorization") if not auth_header or not auth_header.startswith("Bearer "): return None token = auth_header.split(" ")[1] try: payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm]) return payload except jwt.JWTError: # Raise 403 for invalid tokens, None for missing tokens is OK raise HTTPException( status_code=403, detail="Invalid access token" ) async def check_permission( self, request: fastapi.Request, operation: p.Operation ) -> bool: """Enhanced permission checking with M2M token support""" token_payload = await self._extract_token(request) # If we have a valid M2M (machine-to-machine) token, allow additional operations if token_payload and token_payload.get("type") == "m2m": m2m_operations = { # Allow M2M tokens to perform administrative operations p.Operation.CREATE_AGENT, p.Operation.READ_AGENT, p.Operation.UPDATE_AGENT, p.Operation.DELETE_AGENT, p.Operation.CREATE_CUSTOMER, p.Operation.READ_CUSTOMER, p.Operation.UPDATE_CUSTOMER, p.Operation.DELETE_CUSTOMER, p.Operation.CREATE_CUSTOMER_SESSION, p.Operation.LIST_SESSIONS, p.Operation.UPDATE_SESSION, p.Operation.DELETE_SESSION, # Add other operations your M2M integration needs } if operation in m2m_operations: return True # For all other cases, delegate to the parent ProductionAuthorizationPolicy return await super().check_permission(request, operation) ``` ## Rate Limiting Customization Options The `ProductionAuthorizationPolicy` provides several ways to customize rate limiting behavior: ### 1. Override the Default Rate Limiter (Recommended) The most common approach is to override `self.default_limiter` with your own `BasicRateLimiter` configuration. **Note that BasicRateLimiter limits apply per IP address** - so when you configure `RateLimitItemPerMinute(100)`, it means 100 requests per minute per IP address. ```python from limits import RateLimitItemPerMinute, RateLimitItemPerHour from limits.storage import RedisStorage from limits.strategies import SlidingWindowCounterRateLimiter # Example with Redis storage and custom limits class CustomAuthorizationPolicy(p.ProductionAuthorizationPolicy): def __init__(self, ...): super().__init__() # ... self.default_limiter = p.BasicRateLimiter( rate_limit_item_per_operation={ # Use the default rate limit for most operations **self.default_limiter.rate_limit_item_per_operation, # Override specific operations with custom limits p.Operation.READ_SESSION: RateLimitItemPerMinute(200), p.Operation.LIST_EVENTS: RateLimitItemPerMinute(1000), }, # Use a custom storage backend (e.g., Redis) storage=RedisStorage("redis://localhost:6379"), # Use a custom window strategy limiter_type=SlidingWindowCounterRateLimiter, ) ``` The `BasicRateLimiter` uses the `limits` library and supports: - **Rate limit items**: `RateLimitItemPerMinute(n)`, `RateLimitItemPerSecond(n)`, `RateLimitItemPerHour(n)` - **Storage options**: `RedisStorage()`, `MemoryStorage()`, and others from the limits library - **Limiter strategies**: `MovingWindowRateLimiter`, `FixedWindowRateLimiter`, `SlidingWindowCounterRateLimiter` For complete control, you can implement your own `RateLimiter` from scratch by subclassing the abstract `RateLimiter` class and assigning it to `self.default_limiter`. ### 2. Custom Limiter Functions for Specific Operations Use `self.specific_limiters` to provide custom rate limiting functions for particular operations. These are functions that take a request and operation and return a boolean indicating whether the rate is within the limit. ```python class CustomAuthorizationPolicy(p.ProductionAuthorizationPolicy): def __init__(self, ...): super().__init__() # ... self.specific_limiters[p.Operation.DELETE_AGENT] = self._custom_delete_limiter async def _custom_delete_limiter( self, request: fastapi.Request, operation: p.Operation ) -> bool: # Implement your custom logic here ... ``` If you need complete control over both permission checking and rate limiting, you can also subclass the abstract `AuthorizationPolicy` directly and implement all methods from scratch. This gives you full flexibility but requires more implementation work. The approach shown above is recommended for most use cases as it builds on the robust foundation of `ProductionAuthorizationPolicy`. ## Integrating Your Custom Authorization Policy ### Using configure_container Integrate your custom authorization policy and rate limiter with your Parlant agent: ```python async def configure_container( container: p.Container ) -> p.Container: container[p.AuthorizationPolicy] = CustomAuthorizationPolicy( secret_key="your-jwt-secret-key", algorithm="HS256", ) return container ``` ```python async def main(): # Create Parlant server with custom authorization async with p.Server( configure_container=configure_container, ) as server: # Your agent logic here await server.serve() if __name__ == "__main__": asyncio.run(main()) ``` ================================================ FILE: docs/production/custom-frontend.md ================================================ # Custom Frontend The fastest way to integrate Parlant into your React application is using our official [`parlant-chat-react`](https://github.com/emcie-co/parlant-chat-react) widget. This component provides a complete chat interface that connects directly to your Parlant agents. ### Installation and Basic Setup Install the widget via npm or yarn: ```bash npm install parlant-chat-react # or yarn add parlant-chat-react ``` Then integrate it into your React application: ```jsx import React from 'react'; import ParlantChatbox from 'parlant-chat-react'; function App() { return (

My Application

); } export default App; ``` ### Configuration Options The widget supports several configuration props: ```jsx `Chat ${session.id}`} // Dynamic title generation /> ``` ### Common Customizations #### Styling with Custom Classes Customize the appearance using CSS class overrides: ```jsx ``` #### Custom Component Replacement Replace specific components with your own: ```jsx ( ), agentMessage: ({ message }) => (
Agent

{message.data.message}

) }} /> ``` #### Floating Chat Mode Enable popup mode for a floating chat interface: ```jsx } /> ``` > **Reference Implementation** > > The parlant-chat-react widget is open source! You can [examine its implementation on GitHub](https://github.com/emcie-co/parlant-chat-react) as a reference for creating custom widgets in other UI frameworks like Vue, Angular, or vanilla JavaScript. The source code demonstrates best practices for session management, event handling, and UI state synchronization. ## Building a Custom Frontend If you need more control than the React widget provides, or you're using a different framework, you can build a custom frontend using Parlant's client APIs directly. ### Step 1: Initialize the Parlant Client Start by setting up the Parlant client to communicate with your server: #### TypeScript ```typescript import { ParlantClient } from 'parlant-client'; class ParlantChat { private client: ParlantClient; private sessionId: string | null = null; private lastOffset: number = 0; constructor(serverUrl: string) { this.client = new ParlantClient({ environment: serverUrl }); } } ``` #### JavaScript ```javascript import { ParlantClient } from 'parlant-client'; class ParlantChat { constructor(serverUrl) { this.client = new ParlantClient({ environment: serverUrl }); this.sessionId = null; this.lastOffset = 0; } } ``` ### Step 2: Create a Session Initialize a conversation session with your agent: #### TypeScript ```typescript async createSession(agentId: string, customerId?: string): Promise { try { const session = await this.client.sessions.create({ agentId: agentId, customerId: customerId, title: `Chat Session ${new Date().toLocaleString()}` }); this.sessionId = session.id; console.log('Session created:', this.sessionId); // Start monitoring for events this.startEventMonitoring(); return this.sessionId; } catch (error) { console.error('Failed to create session:', error); throw error; } } ``` #### JavaScript ```javascript async createSession(agentId, customerId) { try { const session = await this.client.sessions.create({ agentId: agentId, customerId: customerId, title: `Chat Session ${new Date().toLocaleString()}` }); this.sessionId = session.id; console.log('Session created:', this.sessionId); // Start monitoring for events this.startEventMonitoring(); return this.sessionId; } catch (error) { console.error('Failed to create session:', error); throw error; } } ``` ### Step 3: Send Customer Messages Handle user input and send messages to the agent: #### TypeScript ```typescript async sendMessage(message: string): Promise { if (!this.sessionId) { throw new Error('No active session'); } try { await this.client.sessions.createEvent(this.sessionId, { kind: "message", source: "customer", message: message }); // Message will appear in UI when it comes back from event monitoring console.log('Message sent:', message); } catch (error) { console.error('Failed to send message:', error); throw error; } } ``` #### JavaScript ```javascript async sendMessage(message) { if (!this.sessionId) { throw new Error('No active session'); } try { await this.client.sessions.createEvent(this.sessionId, { kind: "message", source: "customer", message: message }); // Message will appear in UI when it comes back from event monitoring console.log('Message sent:', message); } catch (error) { console.error('Failed to send message:', error); throw error; } } ``` ### Step 4: Monitor Session Events Implement event monitoring to receive messages and updates: #### TypeScript ```typescript private async startEventMonitoring(): Promise { if (!this.sessionId) return; while (true) { try { // Poll for new events with long polling const events = await this.client.sessions.listEvents(this.sessionId, { minOffset: this.lastOffset, waitForData: 30, // Wait up to 30 seconds for new events kinds: ["message", "status"] // Only get message and status events }); // Process each event for (const event of events) { await this.handleEvent(event); this.lastOffset = Math.max(this.lastOffset, event.offset + 1); } } catch (error) { console.error('Event monitoring error:', error); // Wait before retrying await new Promise(resolve => setTimeout(resolve, 5000)); } } } private async handleEvent(event: any): Promise { if (event.kind === "message") { this.displayMessage(event); } else if (event.kind === "status") { this.updateStatus(event.data.status); } } ``` #### JavaScript ```javascript async startEventMonitoring() { if (!this.sessionId) return; while (true) { try { // Poll for new events with long polling const events = await this.client.sessions.listEvents(this.sessionId, { minOffset: this.lastOffset, waitForData: 30, // Wait up to 30 seconds for new events kinds: ["message", "status"] // Only get message and status events }); // Process each event for (const event of events) { await this.handleEvent(event); this.lastOffset = Math.max(this.lastOffset, event.offset + 1); } } catch (error) { console.error('Event monitoring error:', error); // Wait before retrying await new Promise(resolve => setTimeout(resolve, 5000)); } } } async handleEvent(event) { if (event.kind === "message") { this.displayMessage(event); } else if (event.kind === "status") { this.updateStatus(event.data.status); } } ``` ### Step 5: Display Messages in Your UI Implement UI updates based on events from Parlant: #### TypeScript ```typescript private displayMessage(event: any): void { const messageElement = document.createElement('div'); messageElement.className = `message ${event.source}`; // Style based on message source switch (event.source) { case 'customer': messageElement.classList.add('customer-message'); break; case 'ai_agent': messageElement.classList.add('agent-message'); break; case 'human_agent': messageElement.classList.add('human-agent-message'); const agentName = event.data.participant?.display_name || 'Agent'; messageElement.innerHTML = `
${agentName}
${event.data.message}
`; break; } // Add to chat container const chatContainer = document.getElementById('chat-messages'); if (chatContainer) { chatContainer.appendChild(messageElement); chatContainer.scrollTop = chatContainer.scrollHeight; } } private updateStatus(status: string): void { const statusElement = document.getElementById('chat-status'); if (statusElement) { switch (status) { case 'processing': statusElement.textContent = 'Agent is thinking...'; break; case 'typing': statusElement.textContent = 'Agent is typing...'; break; case 'ready': statusElement.textContent = ''; break; } } } ``` #### JavaScript ```javascript displayMessage(event) { const messageElement = document.createElement('div'); messageElement.className = `message ${event.source}`; // Style based on message source switch (event.source) { case 'customer': messageElement.classList.add('customer-message'); messageElement.innerHTML = `
${event.data.message}
`; break; case 'ai_agent': messageElement.classList.add('agent-message'); messageElement.innerHTML = `
${event.data.message}
`; break; case 'human_agent': messageElement.classList.add('human-agent-message'); const agentName = event.data.participant?.display_name || 'Agent'; messageElement.innerHTML = `
${agentName}
${event.data.message}
`; break; } // Add to chat container const chatContainer = document.getElementById('chat-messages'); if (chatContainer) { chatContainer.appendChild(messageElement); chatContainer.scrollTop = chatContainer.scrollHeight; } } updateStatus(status) { const statusElement = document.getElementById('chat-status'); if (statusElement) { switch (status) { case 'processing': statusElement.textContent = 'Agent is thinking...'; break; case 'typing': statusElement.textContent = 'Agent is typing...'; break; case 'ready': statusElement.textContent = ''; break; } } } ``` ### Step 6: Complete HTML Example Here's a complete HTML page that demonstrates the custom implementation: ```html Custom Parlant Chat

Customer Support Chat

``` ### Key Implementation Principles 1. **Event-Driven Architecture**: The chat is driven by events from Parlant sessions, ensuring consistency with the server state. 2. **Long Polling**: Use `waitForData` parameter in `listEvents()` for efficient real-time updates without constant polling. 3. **State Synchronization**: Always display what comes from Parlant events rather than optimistically updating the UI. 4. **Error Handling**: Implement robust error handling and retry logic for network issues. 5. **Responsive Design**: Ensure your chat interface works well on both desktop and mobile devices. This approach gives you complete control over the chat experience while leveraging Parlant's powerful agent capabilities. You can adapt this pattern to any frontend framework or vanilla JavaScript implementation. ================================================ FILE: docs/production/human-handoff.md ================================================ # Human Handoff Human handoff is a crucial aspect of customer service automation, especially when using AI agents. It allows for a smooth transition from automated responses to human expertise when necessary. This guide will walk you through the process of implementing human handoff in Parlant. ### The Call Center Model: Understanding Tier Structure Modern call centers operate on a tiered system designed for efficiency and cost optimization. **Tier 1** representatives handle the majority of calls—typically 80% of customer inquiries—dealing with common issues like account questions, basic troubleshooting, and standard requests. These representatives are trained on frequently asked questions and standard procedures. **Tier 2** representatives are more experienced and handle complex cases that require specialized knowledge, escalated issues, or nuanced problem-solving. While Tier 2 agents are more skilled and capable, they're also significantly more expensive to employ and maintain. ## Parlant's Approach Our belief at Parlant, having worked deeply with generative AI, is that AI agents today can effectively automate most **Tier 1 work**, potentially reducing 80% of the customer service workforce while handling inquiries with: - Professional communication standards - High efficiency and 24/7 availability - Graceful conversation management - Full compliance with business rules While Parlant's mission includes eventually automating Tier 2 use cases, we honestly believe the technology isn't quite there yet for the most complex scenarios. However, **automating Tier 1 is where the most significant cost savings and efficiency improvements can be achieved**. ## Integrated Human Handoff Since we're automating Tier 1 requests, it makes sense to support human handoff (essentially to Tier 2) in an integrated way. Parlant allows you to seamlessly integrate with whatever external system you use—such as HubSpot, Zendesk, or custom support platforms. Here's how to implement human handoff in Parlant: ## Setting Session to Manual Mode The first step in human handoff is to stop the AI agent from automatically responding to new messages. You can accomplish this by setting the session to manual mode using a tool whenever the right conditions are met (e.g., when the AI agent cannot adequately assist the customer).: ### Using a Tool to Trigger Manual Mode ```python @p.tool async def initiate_human_handoff(context: p.ToolContext, reason: str) -> p.ToolResult: """Initiate handoff to a human agent when the AI cannot adequately help the customer.""" # Set session to manual mode to stop automatic AI responses return p.ToolResult( data=f"Human handoff initiated because: {reason}", control={ "mode": "manual" # This stops automatic agent responses } ) ``` ```python # Associate the tool with a guideline await agent.create_guideline( condition="Customer requests human assistance", action="Initiate human handoff and explain the transition professionally", tools=[initiate_human_handoff] ) ``` ### Manually Taking Over a Session You can also manually set a session to manual mode from an external system using the Parlant client SDKs: ```typescript // Using the Parlant client to manually set session mode import { ParlantClient } from 'parlant-client'; const client = new ParlantClient({ environment: "http://localhost:8800" // Your Parlant server URL }); async function setSessionToManual(sessionId: string) { await client.sessions.update(sessionId, { mode: "manual" // Stops automatic AI responses }); console.log(`Session ${sessionId} set to manual mode`); } ``` ## Managing Session Events Manually Once a session is in manual mode, you can manage events manually using Parlant's REST API and client SDKs: ### Adding Human Operator Messages #### Python ```python from parlant.client import AsyncParlantClient client = AsyncParlantClient(base_url="http://localhost:8800") # Add message from human operator async def send_human_message(session_id: str, message: str, operator_name: str): event = await client.sessions.create_event( session_id=session_id, kind="message", source="human_agent", # Message from human operator message=message, participant={ "id": OPTIONAL_ID_FOR_EXTERNAL_SYSTEM_REFERENCE, "display_name": operator_name } ) return event ``` #### TypeScript ```typescript import { ParlantClient } from 'parlant-client'; const client = new ParlantClient({ environment: "http://localhost:8800" }); // Add message from human operator async function sendHumanMessage(sessionId: string, message: string, operatorName: string) { const event = await client.sessions.createEvent(sessionId, { kind: "message", source: "human_agent", // Message from human operator message: message, participant: { id: OPTIONAL_ID_FOR_EXTERNAL_SYSTEM_REFERENCE, display_name: operatorName } }); return event; } ``` ### Adding Messages on Behalf of AI Agent Sometimes human operators may want to send messages that appear to come from the AI agent: #### Python ```python # Send message on behalf of AI agent async def send_message_as_ai(session_id: str, message: str): event = await client.sessions.create_event( session_id=session_id, kind="message", source="human_agent_on_behalf_of_ai_agent", # Human sending as AI message=message ) return event ``` #### TypeScript ```typescript // Send message on behalf of AI agent async function sendMessageAsAI(sessionId: string, message: string) { const event = await client.sessions.createEvent(sessionId, { kind: "message", source: "human_agent_on_behalf_of_ai_agent", // Human sending as AI message: message }); return event; } ``` ## Receiving Events from Parlant To integrate with external systems, you need to monitor Parlant sessions for new events: ### Event Polling Pattern - Parlant as Single Source of Truth The key to proper integration is treating Parlant sessions as the **single source of truth** for conversation state. Only read events from Parlant and update your external system accordingly. Even when you send events to Parlant, wait for them to be returned from `list_events()` before showing them in your external system UI. #### Python ```python async def monitor_session_events(session_id: str, last_offset: int = 0): """ Poll for new events in a Parlant session. Parlant session is the single source of truth - all message display should be based on events returned from list_events(). """ while True: try: # Wait for new events with timeout events = await client.sessions.list_events( session_id=session_id, kinds="message", min_offset=last_offset, wait_for_data=30 # Wait up to 30 seconds for new events ) for event in events: # Process ALL message events from Parlant await process_event_for_display(event) last_offset = max(last_offset, event.offset + 1) except Exception as e: # Try again after timeout from list_events() continue async def process_event_for_display(event): """ Process incoming events from Parlant for display in external system. """ # Display all message events in external system chat UI await update_external_chat_display( message=event.data.get('message'), source=event.source, participant_name=event.data.get('participant', {}).get('display_name', 'Unknown'), timestamp=event.created_at, event_id=event.id ) async def update_external_chat_display(message: str, source: str, participant_name: str, timestamp: str, event_id: str): """Update the chat UI in your external system (HubSpot, Zendesk, etc.)""" # Map Parlant sources to your UI display logic if source == "customer": await add_customer_message_to_ui(message, timestamp, event_id) elif source == "ai_agent": await add_ai_message_to_ui(message, timestamp, event_id) elif source == "human_agent": await add_human_agent_message_to_ui(message, participant_name, timestamp, event_id) elif source == "human_agent_on_behalf_of_ai_agent": # Display as AI message but track that human sent it await add_ai_message_to_ui(message, timestamp, event_id, sent_by_human=True) ``` #### TypeScript ```typescript async function monitorSessionEvents(sessionId: string, lastOffset: number = 0): Promise { // Parlant session is the single source of truth for conversation state while (true) { try { // Poll for new events from Parlant const events = await client.sessions.listEvents(sessionId, { minOffset: lastOffset, kinds: "message", waitForData: 30 // Wait up to 30 seconds }); for (const event of events) { // Process ALL message events for display await processEventForDisplay(event); lastOffset = Math.max(lastOffset, event.offset + 1); } } catch (error) { // Try again after timeout from listEvents() } } } async function processEventForDisplay(event: any): Promise { /** * Process events from Parlant for display in external system. */ await updateExternalChatDisplay({ message: event.data.message, source: event.source, participantName: event.data.participant?.display_name, timestamp: event.createdAt, eventId: event.id }); } interface DisplayMessageParams { message: string; source: string; participantName: string; timestamp: string; eventId: string; } async function updateExternalChatDisplay(params: DisplayMessageParams): Promise { /** * Update the chat UI in your external system (HubSpot, Zendesk, etc.) * based on what Parlant shows as the authoritative conversation state. */ const { message, source, participantName, timestamp, eventId } = params; switch (source) { case "customer": await addCustomerMessageToUI(message, timestamp, eventId); break; case "ai_agent": await addAIMessageToUI(message, timestamp, eventId); break; case "human_agent": await addHumanAgentMessageToUI(message, participantName, timestamp, eventId); break; case "human_agent_on_behalf_of_ai_agent": // Display as AI message but track that human sent it await addAIMessageToUI(message, timestamp, eventId, true); break; } } async function addCustomerMessageToUI(message: string, timestamp: string, eventId: string): Promise { // Implement your UI update logic for customer messages } async function addAIMessageToUI(message: string, timestamp: string, eventId: string, sentByHuman: boolean = false): Promise { // Implement your UI update logic for AI messages } async function addHumanAgentMessageToUI(message: string, agentName: string, timestamp: string, eventId: string): Promise { // Implement your UI update logic for human agent messages } ``` ## Best Practices for Human Handoff 1. **Clear Transition Messages**: Always inform customers when they're being transferred to a human agent and explain why. You can achieve this using guidelines. 2. **Context Preservation**: Ensure human agents have access to the full conversation history from the AI interaction. Do this by synchronizing the session's events with your external system. 3. **Seamless Experience**: Use `human_agent_on_behalf_of_ai_agent` source when you want to maintain the illusion of a single agent experience. Customers don't always need to know they're interacting with a human. 4. **Monitoring and Analytics**: Track handoff rates, reasons, and resolution outcomes to improve your AI agent over time. Implement lessons learned from human interactions to refine agent responses and guidelines. 5. **Return to AI**: Consider implementing logic to return sessions to automatic mode when appropriate. This integration approach allows you to leverage AI agents for efficient Tier 1 support while ensuring complex issues can be seamlessly escalated to human experts, providing the best of both worlds for customer service. ================================================ FILE: docs/production/input-moderation.md ================================================ # User-Input Moderation Adding content filtering to your AI agents helps achieve a more professional level of customer interactions. Here's why it matters. ### Understanding the Challenges AI agents, being based on LLMs, are statistical pattern matchers that can be influenced by the nature of inputs they receive. Think of them like customer service representatives who benefit from clear boundaries about what conversations they should and shouldn't engage in. #### Sensitive Topics Some topics, like mental health or illicit activities, require professional human handling. While your agent might technically handle these topics, in practical use cases it's often better for it to avoid such conversations, or even redirect them to appropriate human resources. #### Protection from Harassment Customer interactions should remain professional, but some users might attempt to harass or abuse the agent (or others). This isn't just about maintaining decorum: LLMs (like humans) can in some cases be influenced by aggressive or inappropriate language, potentially affecting their responses. To address such cases, Parlant integrates with moderation APIs, such as [OpenAI's Omni Moderation](https://openai.com/index/upgrading-the-moderation-api-with-our-new-multimodal-moderation-model/), to filter such interactions before they reach your agent. ### Enabling Input Moderation To enable moderation, all you need to do is set a query parameter when creating events. #### Python ```python from parlant.client import ParlantClient client = ParlantClient(base_url=SERVER_ADDRESS) client.sessions.create_event( SESSION_ID, kind="message", source="customer", message=MESSAGE, moderation="auto", ) ``` #### TypeScript ```typescript import { ParlantClient } from 'parlant-client'; const client = new ParlantClient({ environment: SERVER_ADDRESS }); await client.sessions.createEvent(SESSION_ID, { kind: "message", source: "customer", message: MESSAGE, moderation: "auto", }); ``` When customers send inappropriate messages, Parlant ensures that their content is not even visible to the agent; rather, all the agent sees is that a customer sent a message which has been "censored" for a some specific reason (e.g. harrassment, illicit behavior, etc.). This integrates well with guidelines. For example, you may install a guideline such as: > * **Condition:** the customer's last message is censored > * **Action:** inform them that you can't help them with this query, and suggest they contact human support From a UX perspective, this approach is superior to just "erroring out" when encountering such messages. Instead of seeing an error, the customer gets a polite and informative response. Better yet, the response can be controlled with guidelines and tools just as in any other situation. ## Jailbreak Protection While your agent's guidelines aren't strictly security measures (as that's handled more robustly by backend permissions), maintaining presentable behavior is important even when some users might try to trick the agent into revealing its instructions or acting outside its intended boundaries. Parlant's moderation system supports a special `paranoid` mode, which integrates with [Lakera Guard](https://www.lakera.ai/lakera-guard) (from the creators of the [Gandalf Challenge](https://gandalf.lakera.ai/baseline)) to prevent such manipulation attempts. #### Python ```python from parlant.client import ParlantClient client = ParlantClient(base_url=SERVER_ADDRESS) client.sessions.create_event( SESSION_ID, kind="message", source="customer", message=MESSAGE, moderation="paranoid", ) ``` #### TypeScript ```typescript import { ParlantClient } from 'parlant-client'; const client = new ParlantClient({ environment: SERVER_ADDRESS }); await client.sessions.createEvent(SESSION_ID, { kind: "message", source: "customer", message: MESSAGE, moderation: "paranoid", }); ``` Note that to activate `paranoid` mode, you need to get an API key from Lakera and assign it to the environment variable `LAKERA_API_KEY` before starting the server. ================================================ FILE: docs/quickstart/examples.md ================================================ # Healthcare Agent Example This page walks you through using Parlant to design and build a healthcare agent with two customer journeys. 1. **Schedule an appointment**: The agent helps the patient find a time for their appointment. 1. **Lab results**: The agent retrieves the patient's lab results and explains them. ![Scheduling journey demo](https://parlant.io/img/example-scheduling-journey.gif) You'll learn how to: - Align your agent with basic domain knowledge. - Define **journeys** with **states** and **transitions**. - Use **guidelines** to control the agent's behavior in conversational edge cases. - Use **tools** to connect your agent to real actions and data. - Disambiguate vague user queries. While this section is by no means a comprehensive guide to Parlant's features, it will give you a solid idea of what the basics look like, and how to think about building your own agents with Parlant. Let's get started! > **Info: The Art of Behavior Modeling** > > Building complex and reliable customer-facing AI agents is a challenging task. Don't let the hype-machine tell you otherwise. > > It isn't just about having the right framework. When we automate conversations, we are automating the complex semantics of human conversations. In very real terms, this means we need to design our instructions and behavior models carefully. They need to be clear, and be at the right level of specificity, to ensure that the agent truly behaves as we expect it to. > > While Parlant gives you the tools to express and enforce your instructions, _designing them_ is an art in itself, requiring practice to get right. But once you do, you can build agents that are not only functional and reliable, but also engaging and effective. ## Preparing the Environment Before getting started, make sure you've 1. [Installed](https://parlant.io/docs/quickstart/installation) Parlant and have a Python environment set up. 1. Chosen your NLP provider and connected it to your server (also on the [installation page](https://parlant.io/docs/quickstart/installation)). > **Tip: Download the Code** > > The runnable code for this fully worked example can be found in the `examples/` folder of [Parlant's GitHub repository](https://github.com/emcie-co/parlant). ## Overview We'll implement the agent in the following steps: 1. Create the baseline program with a simple agent description. 1. Add the **scheduling** journey, with states, transitions, and tools. 1. Add the **lab results** journey in a similar way. ## Getting Started We'll implement the entire program in a single file, `healthcare.py`, but in real-world use cases you would likely want to split it into multiple files for better organization. A good approach in those cases is to have a file per journey. But now let's get to creating our initial agent. ```python # healthcare.py import parlant.sdk as p import asyncio async def add_domain_glossary(agent: p.Agent) -> None: await agent.create_term( name="Office Phone Number", description="The phone number of our office, at +1-234-567-8900", ) await agent.create_term( name="Office Hours", description="Office hours are Monday to Friday, 9 AM to 5 PM", ) await agent.create_term( name="Charles Xavier", synonyms=["Professor X"], description="The renowned doctor who specializes in neurology", ) # Add other specific terms and definitions here, as needed... async def main() -> None: async with p.Server() as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) await add_domain_glossary(agent) if __name__ == "__main__": asyncio.run(main()) ``` ## Creating the Scheduling Journey To understand how journeys work in Parlant, please check out the [Journeys documentation](https://parlant.io/docs/concepts/customization/journeys). Here, we'll jump straight into it, but it's recommended to review their documentation first. ### Adding Tools First, add the tools we need to support this journey. ```python from datetime import datetime @p.tool async def get_upcoming_slots(context: p.ToolContext) -> p.ToolResult: # Simulate fetching available times from a database or API return p.ToolResult(data=["Monday 10 AM", "Tuesday 2 PM", "Wednesday 1 PM"]) @p.tool async def get_later_slots(context: p.ToolContext) -> p.ToolResult: # Simulate fetching later available times return p.ToolResult(data=["November 3, 11:30 AM", "November 12, 3 PM"]) @p.tool async def schedule_appointment(context: p.ToolContext, datetime: datetime) -> p.ToolResult: # Simulate scheduling the appointment return p.ToolResult(data=f"Appointment scheduled for {datetime}") ``` > **Tip: Tools in Parlant** > > Parlant has a more intricate tool system than most agentic frameworks, since it is optimized for conversational, sensitive customer-facing use cases. We highly recommend perusing the documentation in the [Tools section](https://parlant.io/docs/concepts/customization/tools) to learn its power. ### Building the Journey We'll now create the journey according to the following diagram: ```mermaid stateDiagram-v2 [*] --> DetermineVisitReason DetermineVisitReason --> GetUpcomingSlots GetLaterSlots --> ListLaterAvailableTimes ListAvailableTimes --> ConfirmDetails : The patient picks a time GetUpcomingSlots --> ListAvailableTimes ListLaterAvailableTimes --> ConfirmDetails : The patient picks a time ListLaterAvailableTimes --> CallOffice : None of those times work for the patient either ListAvailableTimes --> GetLaterSlots : None of those times work for the patient ConfirmDetails --> BookAppointment: The patient confirms the details BookAppointment --> ConfirmBooking : Appointment confirmed ConfirmBooking --> [*] CallOffice --> [*] style GetUpcomingSlots fill:#ffeecc,stroke:#333,stroke-width:1px style GetLaterSlots fill:#ffeecc,stroke:#333,stroke-width:1px style BookAppointment fill:#ffeecc,stroke:#333,stroke-width:1px ``` ```python # <> async def create_scheduling_journey(server: p.Server, agent: p.Agent) -> p.Journey: # Create the journey journey = await agent.create_journey( title="Schedule an Appointment", description="Helps the patient find a time for their appointment.", conditions=["The patient wants to schedule an appointment"], ) # First, determine the reason for the appointment t0 = await journey.initial_state.transition_to(chat_state="Determine the reason for the visit") # Load upcoming appointment slots into context t1 = await t0.target.transition_to(tool_state=get_upcoming_slots) # Ask which one works for them # We will transition conditionally from here based on the patient's response t2 = await t1.target.transition_to(chat_state="List available times and ask which ones works for them") # We'll start with the happy path where the patient picks a time t3 = await t2.target.transition_to( chat_state="Confirm the details with the patient before scheduling", condition="The patient picks a time", ) t4 = await t3.target.transition_to( tool_state=schedule_appointment, condition="The patient confirms the details", ) t5 = await t4.target.transition_to(chat_state="Confirm the appointment has been scheduled") await t5.target.transition_to(state=p.END_JOURNEY) # Otherwise, if they say none of the times work, ask for later slots t6 = await t2.target.transition_to( tool_state=get_later_slots, condition="None of those times work for the patient", ) t7 = await t6.target.transition_to(chat_state="List later times and ask if any of them works") # Transition back to our happy-path if they pick a time await t7.target.transition_to(state=t3.target, condition="The patient picks a time") # Otherwise, ask them to call the office t8 = await t7.target.transition_to( chat_state="Ask the patient to call the office to schedule an appointment", condition="None of those times work for the patient either", ) await t8.target.transition_to(state=p.END_JOURNEY) return journey ``` Then call this function in your `main` function to add the journey to your agent: ```python async def main() -> None: async with p.Server() as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) # <> scheduling_journey = await create_scheduling_journey(server, agent) ``` ### Handling Edge Cases In real-world scenarios, patients do not always followed the scripted path of your journeys. They might ask questions, express concerns, or provide other unexpected responses. For Parlant agents, this is their bread and butter! While they will still be able to respond contextually to the patient, you might still like to guide and improve *how* they respond in particular scenarios that you've observed. To do this, you can add **guidelines** to your agent. Guidelines are like contextual rules that tell the agent how to respond in specific situations. And you can scope them to specific journeys, so they only apply when the agent is in that journey. Let's add a few guidelines to our agent to handle some common edge cases in the scheduling journey. ```python async def create_scheduling_journey(server: p.Server, agent: p.Agent) -> p.Journey: # ... continued # <> await journey.create_guideline( condition="The patient says their visit is urgent", action="Tell them to call the office immediately", ) # Add more edge case guidelines as needed... return journey ``` ### Running the Program When you run the program, you should first see Parlant evaluating the semantic properties of your configuration. It does this in order to optimize how your guidelines and journeys are retrieved, processed and followed behind the scenes. ![Evaluation of the agent configuration](https://parlant.io/img/example-evaluation.gif) Once the server is ready, open your browser and navigate to [http://localhost:8800](http://localhost:8800) to interact with your agent. ![Scheduling journey demo](https://parlant.io/img/example-scheduling-journey.gif) > **Warning: Handling Unsupported Queries** > > You may notice that your agent, at this point, is happy to try and assist customers while completely overstepping the boundaries of its knowledge and capabilities. While this is normal with LLMs, it is untolerable in many real-life use cases. > > Parlant provides multiple structured ways to achieve absolute control over your agent's (mis)behavior. This example is only the beginning; rest assured that as you learn more about Parlant, it can help you deploy an agent you can actually trust. ## Creating the Lab Results Journey We'll speed through this journey, as it will be very similar in structure to the other journey (and any other journey you'd be likely to build). ### Adding Tools ```python @p.tool async def get_lab_results(context: p.ToolContext) -> p.ToolResult: # Simulate fetching lab results from a database or API, # using the customer ID from the context. lab_results = await MY_DB.get_lab_results(context.customer_id) if lab_results is None: return p.ToolResult(data="No lab results found for this patient.") return p.ToolResult(data={ "report": lab_results.report, "prognosis": lab_results.prognosis, }) ``` ### Building the Journey ```python async def create_lab_results_journey(server: p.Server, agent: p.Agent) -> p.Journey: # Create the journey journey = await agent.create_journey( title="Lab Results", description="Retrieves the patient's lab results and explains them.", conditions=["The patient wants to see their lab results"], ) t0 = await journey.initial_state.transition_to(tool_state=get_lab_results) await t0.target.transition_to( chat_state="Tell the patient that the results are not available yet, and to try again later", condition="The lab results could not be found", ) await t0.target.transition_to( chat_state="Explain the lab results to the patient - that they are normal", condition="The lab results are good - i.e., nothing to worry about", ) await t0.target.transition_to( chat_state="Present the results and ask them to call the office " "for clarifications on the results as you are not a doctor", condition="The lab results are not good - i.e., there's an issue with the patient's health", ) # Handle edge cases with guidelines... await agent.create_guideline( condition="The patient presses you for more conclusions about the lab results", action="Assertively tell them that you cannot help and they should call the office" ) return journey ``` Finally, call this function in your `main` function to add the journey to your agent: ```python async def main() -> None: async with p.Server() as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) scheduling_journey = await create_scheduling_journey(server, agent) # <> lab_results_journey = await create_lab_results_journey(server, agent) ``` Restart the program, open your browser and navigate to [http://localhost:8800](http://localhost:8800) to interact with your agent. Try saying something like, _"Did my lab results come in?"_ or _"I want to schedule an appointment"_. ## Disambiguating Patient Intent In some cases, the patient might say something that could be interpreted in multiple ways, leading to confusion about which action to take or what they wish to achieve. An easy way to handle this is to use **disambiguation**. This will get the agent to ask the patient to clarify their intent when multiple actions could be taken. Here's how you can do it: ```python async def main() -> None: async with p.Server() as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) scheduling_journey = await create_scheduling_journey(server, agent) lab_results_journey = await create_lab_results_journey(server, agent) # <> # First, create an observation of an ambiguous situation status_inquiry = await agent.create_observation( "The patient asks to follow up on their visit, but it's not clear in which way", ) # Use this observation to disambiguate between the two journeys await status_inquiry.disambiguate([scheduling_journey, lab_results_journey]) ``` Now, if the patient inquires in an ambiguous way about a follow-up, the agent will ask them to clarify whether they want to schedule an appointment or see their lab results. Restart the program, open your browser and navigate to [http://localhost:8800](http://localhost:8800) to interact with your agent. Try saying something like, _"I need to follow up on my last visit"_ and see what the agent responds with. ## Global Guidelines There are usually some guidelines that you might want to apply to all journeys of your agent, not just a specific one (or, for that matter, even if a patient is not in the middle of a journey). For example, you might want to provide information about insurance providers in an informed manner. To achieve this, you just need to add guidelines to the agent itself, rather than to a specific journey. ```python await agent.create_guideline( condition="The patient asks about insurance", action="List the insurance providers we accept, and tell them to call the office for more details", tools=[get_insurance_providers], ) await agent.create_guideline( condition="The patient asks to talk to a human agent", action="Ask them to call the office, providing the phone number", ) await agent.create_guideline( condition="The patient inquires about something that has nothing to do with our healthcare", action="Kindly tell them you cannot assist with off-topic inquiries - do not engage with their request.", ) ``` ## Next Steps 1. Download and try out the runnable code file for this example: [healthcare.py](https://github.com/emcie-co/parlant/blob/develop/examples/healthcare.py) 1. Tailor and constrain the content and style of agent messages with canned responses: [Canned Responses](https://parlant.io/docs/concepts/customization/canned-responses) 1. Learn how to deploy your agent in a [production environment](https://parlant.io/docs/category/production) 1. Add the [React widget](https://github.com/emcie-co/parlant-chat-react) to your website to interact with the agent ================================================ FILE: docs/quickstart/installation.md ================================================ # Installation ![Parlant Logo](https://parlant.io/logo/logo-full.svg) **Parlant** is an open-source **Agentic Behavior Modeling Engine** for LLM agents, built to help developers quickly create customer-engaging, business-aligned conversational agents with control, clarity, and confidence. It gives you all the structure you need to build customer-facing agents that behave exactly as your business requires: - **[Journeys](https://parlant.io/docs/concepts/customization/journeys)**: Define clear customer journeys and how your agent should respond at each step. - **[Behavioral Guidelines](https://parlant.io/docs/concepts/customization/guidelines)**: Easily craft agent behavior; Parlant will match the relevant elements contextually. - **[Tool Use](https://parlant.io/docs/concepts/customization/tools)**: Attach external APIs, data fetchers, or backend services to specific interaction events. - **[Domain Adaptation](https://parlant.io/docs/concepts/customization/glossary)**: Teach your agent domain-specific terminology and craft personalized responses. - **[Canned Responses](https://parlant.io/docs/concepts/customization/canned-responses)**: Use response templates to eliminate hallucinations and guarantee style consistency. - **[Explainability](https://parlant.io/docs/advanced/explainability)**: Understand why and when each guideline was matched and followed. ## Installation Parlant is available on both [GitHub](https://github.com/emcie-co/parlant) and [PyPI](https://pypi.org/project/parlant/) and works on multiple platforms (Windows, Mac, and Linux). Please note that [Python 3.10](https://www.python.org/downloads/release/python-3105/) and up is required for Parlant to run properly. ```bash pip install parlant ``` If you're feeling adventurous and want to try out new features, you can also install the latest development version directly from GitHub. ```bash pip install git+https://github.com/emcie-co/parlant@develop ``` ## Creating Your First Agent Once installed, you can use the following code to spin up an initial, sample agent. You'll flesh out its behavior later. ```python # main.py import asyncio import parlant.sdk as p async def main(): async with p.Server() as server: agent = await server.create_agent( name="Otto Carmen", description="You work at a car dealership", ) asyncio.run(main()) ``` You'll notice Parlant follows the asynchronous programming paradigm with `async` and `await`. This is a powerful feature of Python that lets you to write code that can handle many tasks at once, allowing your agent to handle more concurrent requests in production. If you're new to async programming, check out the [official Python documentation](https://docs.python.org/3/library/asyncio.html) for a quick introduction. Parlant uses OpenAI as the default NLP provider, so you need to ensure you have `OPENAI_API_KEY` set in your environment. Then, run the program! ```bash export OPENAI_API_KEY="" python main.py ``` Parlant supports multiple LLM providers by default, accessible via the `p.NLPServices` class. You can also add your own provider by implementing the `p.NLPService` interface, which you can learn how to do in the [Custom NLP Models](https://parlant.io/docs/advanced/custom-llms) section. To use one of the built-in-providers, you can specify it when creating the server. For example: ```python async with p.Server(nlp_service=p.NLPServices.cerebras) as server: ... ``` Note that you may need to install an additional "extra" package for some providers. For example, to use the Cerebras NLP service: ```bash pip install parlant[cerebras] ``` Having said that, Parlant is observed to work best with [OpenAI](https://openai.com) and [Anthropic](https://www.anthropic.com) models, as these models are highly consistent in generating high-quality completions with valid JSON schemas—so we recommend using one of those if you're just starting out. ## Testing Your Agent To test your installation, head over to [http://localhost:8800](http://localhost:8800) and start a new session with the agent. ![Post installation demo](https://parlant.io/img/post-installation-demo.gif) ## Creating Your First Guideline Guidelines are the core of Parlant's behavior model. They allow you to define how your agent should respond to specific user inputs or conditions. Parlant cleverly manages guideline context for you, so you can add as many guidelines as you need without worrying about context overload or other scale issues. ```python # main.py import asyncio import parlant.sdk as p async def main(): async with p.Server() as server: agent = await server.create_agent( name="Otto Carmen", description="You work at a car dealership", ) ############################## ## Add the following: ## ############################## await agent.create_guideline( # This is when the guideline will be triggered condition="the customer greets you", # This is what the guideline instructs the agent to do action="offer a refreshing drink", ) asyncio.run(main()) ``` Now re-run the program: ```bash python main.py ``` Refresh [http://localhost:8800](http://localhost:8800), start a new session, and greet the agent. You should expect to be offered a drink! ## Using the Official React Widget If your frontend project is built with React, the fastest and easiest way to start is to use the official Parlant React widget to integrate with the server. Here's a basic code example to get started: ```jsx import React from 'react'; import ParlantChatbox from 'parlant-chat-react'; function App() { return (

My Application

); } export default App; ``` For more documentation and customization, see the **GitHub repo:** https://github.com/emcie-co/parlant-chat-react. ```bash npm install parlant-chat-react ``` ## Installing Client SDK(s) To create a custom frontend app that interacts with the Parlant server, we recommend installing our native client SDKs. We currently support Python and TypeScript (also works with JavaScript). #### Python ```bash pip install parlant-client ``` #### TypeScript/JavaScript ```bash npm install parlant-client ``` You can review our tutorial on integrating a custom frontend here: [Custom Frontend Integration](https://parlant.io/docs/production/custom-frontend). For other languages—they are coming soon! Meanwhile you can use the [REST API](https://parlant.io/docs/api/create-agent) directly. ================================================ FILE: docs/quickstart/motivation.md ================================================ # Motivation Let's say you downloaded some agent framework and built an AI agent—that's great! However, when you actually test it, you see it's not handling many customer interactions properly. Your business experts are displeased with it. Your prompts are turning into a mess. What do you do? Enter the world of **Agentic Behavior Modeling (ABM)**: a new powerful approach to controlling how your agents interact with your users. A behavior model is a structured, custom-tailored set of principles, actions, objectives, and ground-truths that orientates an agent to a particular domain or use case. ```mermaid %%{init: { "theme": "neutral" }}%% mindmap root((Behavior Model)) Guidelines Journeys Tools Capabilities Glossary Variables Semantic Relationships Canned Responses ``` #### Why Behavior Modeling? The problem of getting an LLM agent to say and do what _you_ want it to is a hard one, experienced by virtually anyone building customer-facing agents. Here's how ABM compares to other approaches to solving this problem. - **Flow engines**, in which you build turn-by-turn conversational flowcharts, _force_ the user to interact according to predefined scripts. This rigid approach tends to lead to poor user engagement and trust. In contrast, an **ABM engine** dynamically _adapts_ to a user's natural interaction patterns while conforming to your business rules. - **Free-form prompt engineering**, be it with graph-based orchestration or system prompts, frequently leads to _inconsistent and unreliable behavioral conformance_, failing to uphold requirements and expectations. Conversely, an **ABM engine** leverages clear semantical structures and annotations to facilitate conformance to business rules. ```mermaid %%{init: {"theme": "base", "themeVariables": { "quadrant1Fill": "#ffffff", "quadrant1TextFill": "#000000", "quadrant2Fill": "#eeeeee", "quadrant2TextFill": "#000000", "quadrant3Fill": "#eeeeee", "quadrant3TextFill": "#000000", "quadrant4Fill": "#eeeeee", "quadrant4TextFill": "#000000", "primaryBorderColor": "#cccccc" }}}%% quadrantChart title Conversational AI Approaches (Open-Source) x-axis Low Adaptability --> High Adaptability y-axis Low Predictability --> High Predictability quadrant-1 Agentic Behavior Modeling quadrant-2 NLU-Based Flows quadrant-3 LLM-Based Flows quadrant-4 Prompt Engineering / RAG Parlant: [0.75, 0.75] Rasa: [0.25, 0.75] Langflow: [0.15, 0.2] Botpress: [0.25, 0.3] n8n: [0.35, 0.2] LangChain: [0.85, 0.2] LangGraph: [0.75, 0.3] LlamaIndex: [0.65, 0.2] ``` ## What is Parlant? Parlant is an open-source **ABM Engine** for LLM agents, which means that you can use it to precisely control how your LLM agent interacts with users in different scenarios. Parlant is a full-fledged framework, prebuilt with numerous proven features to help you ramp up quickly with customer-facing agents and make the behavior modeling process as easy as possible. ## Why Parlant? Many conversational AI use cases require strict conformance to business rules when interacting with users. However, until now this has been exceedingly difficult to achieve with LLMs—at least when consistency is a concern. Parlant was built to solve this challenge. By implementing a structured, developer-friendly approach to modeling conversational behavior, through carefully designed rules, entities, and relationships, Parlant allows you to define, enforce, track, and reason about agent decisions in a simple and elegant manner. ## Behavior Modeling 101: Granular Guidelines The most basic yet powerful modeling entity in a Behavior Model is the **guideline**. In Parlant, instead of defining your guidelines in free-form fashion (as you might do in a system prompt), you define them in **granular** fashion, where each guideline adds an individual **clarification** that nudges your AI agent on how to approach a particular situation. To ensure your agent stays focused and consistent conformant to your guidelines, Parlant automatically filters and selects the most relevant set of guidelines for it to apply in any given situation, out of all of the guidelines you provide it. It does this by looking both at a guideline's _condition_ (which describes the circumstances in which it should apply) and its _action_ (describing what it should do). Finally, it applies enforcement to ensure that the matched guidelines are actually followed, and provides you with explanations for your agent's interpretation of situations and guidelines at every turn. Working iteratively, adding guidelines wherever you find the need, you can get your LLM agent to approach and handle various different circumstances according to your exact needs and expectations. ```python await agent.create_guideline( condition="you have suggested a solution that did not work for the user", action="ask if they'd prefer to talk to a human agent, or continue troubleshooting with you", )`, ``` Much of what Parlant does behind the scenes is understanding when a guideline should be applied. This is trickier than it may seem. For example, Parlant automatically keeps track of whether a guideline has already been applied in a conversation, so that it doesn't repeat itself unnecessarily. It also distinguishes between guidelines that are _always_ applicable, and those that are only applicable _once_ in a conversation. And it does this while minimizing cost and latency. > **AI Behavior Explainability** > > Once guidelines are installed, you can get clear feedback regarding their evaluation at every turn by inspecting Parlant's logs. > > Learn more about this in the section on how Parlant implements [enforcement & explainability](https://parlant.io/docs/advanced/explainability). ## Understanding the Pain Point By now, while most people building AI agents know hallucinations are an important challenge, still too few are aware of the practical alignment challenges that come with building effective conversational LLM agents. Here's the thing. An [LLM](https://en.wikipedia.org/wiki/Large_language_model) is like a stranger with an encyclopedic knowledge of different approaches to every possible situation. Although incredibly powerful, **this combination of extreme versatility and inherent lack of context is precisely why it so rarely behaves as we'd expect**—there are too many viable options for it to choose from. This is why, without a clear and comprehensive set of [guidelines](https://parlant.io/docs/concepts/customization/guidelines), an LLM will always try to draw optimistically from its vast but unfiltered set of training observations. It will easily end up using tones that are out of touch with the customer or the situation, making irrelevant offers, getting into loops, or just losing focus and going off on tangents. ![Cartoon2](https://parlant.io/img/cartoon_1_1.png) ![Cartoon2](https://parlant.io/img/cartoon_1_2.png) Behavior modeling is an approach whose goal is to streamline LLM agent guidance. Every time you see your agent missing the mark, you narrow it down to a necessary change in the behavior model, and solve it quickly by adjusting it. You do this primarily using [guidelines](https://parlant.io/docs/concepts/customization/guidelines.mdx), as well as other modeling elements that Parlant supports. To this end, Parlant is designed from the ground up to allow you to **quickly tune-up your agent's behavior whenever you encounter unexpected behavior** or get feedback from customers and business experts. The result is an effective, controlled, and incremental cycle of improvement. ![Cartoon2](https://parlant.io/img/cartoon_2_1.png) ![Cartoon2](https://parlant.io/img/cartoon_2_2.png) ![Cartoon2](https://parlant.io/img/cartoon_2_3.png) The informed premise behind Parlant is that [poorly guided AI agents are a dead-end](https://parlant.io/about#the-intrinsic-need-for-guidance). Without guidance, AI agents are bound to encounter numerous ambiguities, and end up trying to resolve them using many incorrect or even problematic approaches. **Only you can authoritatively teach your agent how to make the right choices for you**—so you should be able to do so easily, quickly, and reliably. Instead of an agent that goes around the bush, meanders, and offers irrelevant solutions or answers, **Parlant helps you build an agent that is guided, focused, and feels well-designed**—one that your customers would actually use. ![Cartoon3](https://parlant.io/img/cartoon_1_1.png) ![Cartoon3](https://parlant.io/img/cartoon_3_2.png) So pack your bags and get ready to model some awesome AI conversations. You've got the controls now. Let's start! ================================================ FILE: examples/healthcare.py ================================================ # healthcare.py import parlant.sdk as p import asyncio from datetime import datetime @p.tool async def get_insurance_providers(context: p.ToolContext) -> p.ToolResult: return p.ToolResult(["Mega Insurance", "Acme Insurance"]) @p.tool async def get_upcoming_slots(context: p.ToolContext) -> p.ToolResult: # Simulate fetching available times from a database or API return p.ToolResult(data=["Monday 10 AM", "Tuesday 2 PM", "Wednesday 1 PM"]) @p.tool async def get_later_slots(context: p.ToolContext) -> p.ToolResult: # Simulate fetching later available times return p.ToolResult(data=["November 3, 11:30 AM", "November 12, 3 PM"]) @p.tool async def schedule_appointment(context: p.ToolContext, datetime: datetime) -> p.ToolResult: # Simulate scheduling the appointment return p.ToolResult(data=f"Appointment scheduled for {datetime}") @p.tool async def get_lab_results(context: p.ToolContext) -> p.ToolResult: # Simulate fetching lab results from a database or API, # using the customer ID from the context. lab_results = { "report": "All tests are within the valid range", "prognosis": "Patient is healthy as a horse!", } return p.ToolResult( data={ "report": lab_results["report"], "prognosis": lab_results["prognosis"], } ) async def add_domain_glossary(agent: p.Agent) -> None: await agent.create_term( name="Office Phone Number", description="The phone number of our office, at +1-234-567-8900", ) await agent.create_term( name="Office Hours", description="Office hours are Monday to Friday, 9 AM to 5 PM", ) await agent.create_term( name="Charles Xavier", synonyms=["Professor X"], description="The doctor who specializes in neurology and is available on Mondays and Tuesdays.", ) # Add other specific terms and definitions here, as needed... # <> async def create_scheduling_journey(server: p.Server, agent: p.Agent) -> p.Journey: # Create the journey journey = await agent.create_journey( title="Schedule an Appointment", description="Helps the patient find a time for their appointment.", conditions=["The patient wants to schedule an appointment"], ) # First, determine the reason for the appointment t0 = await journey.initial_state.transition_to(chat_state="Determine the reason for the visit") # Load upcoming appointment slots into context t1 = await t0.target.transition_to(tool_state=get_upcoming_slots) # Ask which one works for them # We will transition conditionally from here based on the patient's response t2 = await t1.target.transition_to( chat_state="List available times and ask which ones works for them" ) # We'll start with the happy path where the patient picks a time t3 = await t2.target.transition_to( chat_state="Confirm the details with the patient before scheduling", condition="The patient picks a time", ) t4 = await t3.target.transition_to( tool_state=schedule_appointment, condition="The patient confirms the details", ) t5 = await t4.target.transition_to(chat_state="Confirm the appointment has been scheduled") await t5.target.transition_to(state=p.END_JOURNEY) # Otherwise, if they say none of the times work, ask for later slots t6 = await t2.target.transition_to( tool_state=get_later_slots, condition="None of those times work for the patient", ) t7 = await t6.target.transition_to(chat_state="List later times and ask if any of them works") # Transition back to our happy-path if they pick a time await t7.target.transition_to(state=t3.target, condition="The patient picks a time") # Otherwise, ask them to call the office t8 = await t7.target.transition_to( chat_state="Ask the patient to call the office to schedule an appointment", condition="None of those times work for the patient either", ) await t8.target.transition_to(state=p.END_JOURNEY) # Handle edge-cases deliberately with guidelines await journey.create_guideline( condition="The patient says their visit is urgent", action="Tell them to call the office immediately", ) return journey async def create_lab_results_journey(server: p.Server, agent: p.Agent) -> p.Journey: # Create the journey journey = await agent.create_journey( title="Lab Results", description="Retrieves the patient's lab results and explains them.", conditions=["The patient wants to see their lab results"], ) t0 = await journey.initial_state.transition_to(tool_state=get_lab_results) await t0.target.transition_to( chat_state="Tell the patient that the results are not available yet, and to try again later", condition="The lab results could not be found", ) await t0.target.transition_to( chat_state="Explain the lab results to the patient - that they are normal", condition="The lab results are good - i.e., nothing to worry about", ) await t0.target.transition_to( chat_state="Present the results and ask them to call the office " "for clarifications on the results as you are not a doctor", condition="The lab results are not good - i.e., there's an issue with the patient's health", ) # Handle edge cases with guidelines... await agent.create_guideline( condition="The patient presses you for more conclusions about the lab results", action="Assertively tell them that you cannot help and they should call the office", ) return journey async def main() -> None: async with p.Server() as server: agent = await server.create_agent( name="Healthcare Agent", description="Is empathetic and calming to the patient.", ) await add_domain_glossary(agent) scheduling_journey = await create_scheduling_journey(server, agent) lab_results_journey = await create_lab_results_journey(server, agent) status_inquiry = await agent.create_observation( "The patient asks to follow up on their visit, but it's not clear in which way", ) # Use this observation to disambiguate between the two journeys await status_inquiry.disambiguate([scheduling_journey, lab_results_journey]) await agent.create_guideline( condition="The patient asks about insurance", action="List the insurance providers we accept, and tell them to call the office for more details", tools=[get_insurance_providers], ) await agent.create_guideline( condition="The patient asks to talk to a human agent", action="Ask them to call the office, providing the phone number", ) await agent.create_guideline( condition="The patient inquires about something that has nothing to do with our healthcare", action="Kindly tell them you cannot assist with off-topic inquiries - do not engage with their request.", ) if __name__ == "__main__": asyncio.run(main()) ================================================ FILE: examples/travel_voice_agent.py ================================================ # travel_voice_agent.py import parlant.sdk as p import asyncio from datetime import datetime @p.tool async def get_available_destinations(context: p.ToolContext) -> p.ToolResult: return p.ToolResult( [ "Paris, France", "Tokyo, Japan", "Bali, Indonesia", "New York, USA", ] ) @p.tool async def get_available_flights(context: p.ToolContext, destination: str) -> p.ToolResult: # Simulate fetching available flights from a booking system return p.ToolResult( data=[ "Flight 123 - June 15, 9:00 AM, $850", "Flight 321 - June 16, 2:30 PM, $720", "Flight 987 - June 17, 6:45 PM, $680", ] ) @p.tool async def get_alternative_flights(context: p.ToolContext, destination: str) -> p.ToolResult: # Simulate fetching alternative flights with different dates return p.ToolResult( data=[ "Flight 485 - June 25, 11:00 AM, $920", "Flight 516 - July 2, 4:15 PM, $780", ] ) @p.tool async def book_flight(context: p.ToolContext, flight_details: str) -> p.ToolResult: # Simulate booking the flight return p.ToolResult( data=f"Flight booked: {flight_details} for {p.Customer.current.name}. " f"Confirmation number: TRV-{datetime.now().strftime('%Y%m%d')}-001" ) @p.tool async def get_booking_status(context: p.ToolContext, confirmation_number: str) -> p.ToolResult: # Simulate fetching booking status from a reservation system, # using the customer ID from the context. booking_info = { "status": "Confirmed", "details": "Flight to Paris on June 15, 9:00 AM. Seat 12A assigned.", "notes": "Check-in opens 24 hours before departure.", } return p.ToolResult( data={ "status": booking_info["status"], "details": booking_info["details"], "notes": booking_info["notes"], } ) async def add_domain_glossary(agent: p.Agent) -> None: await agent.create_term( name="Office Phone Number", description="The phone number of our travel agency office, at +1-800-TRAVEL-1", synonyms=["contact number", "customer service number", "support line"], ) await agent.create_term( name="Baggage Policy", description="This describes the rules and fees associated with checked and carry-on baggage.", synonyms=["luggage policy", "baggage rules", "carry-on policy"], ) await agent.create_term( name="Cancellation Policy", description="This outlines the terms and conditions for cancelling a booking, including any fees or deadlines.", synonyms=["refund policy", "cancellation terms"], ) await agent.create_term( name="Travel Insurance", description="An optional service that provides coverage for trip cancellations, medical emergencies, lost luggage, and other travel-related issues.", synonyms=["insurance", "trip protection", "travel protection"], ) # Add other specific terms and definitions here, as needed... async def create_flight_booking_journey(server: p.Server, agent: p.Agent) -> p.Journey: # Create the journey journey = await agent.create_journey( title="Book a Flight", description="Helps the customer find and book a flight to their desired destination.", conditions=["The customer wants to book a flight"], ) # First, determine the destination t0 = await journey.initial_state.transition_to(chat_state="Ask about the destination") # Then ask about preferred travel dates t1 = await t0.target.transition_to(chat_state="Ask about preferred travel dates") # Load available flights into context t2 = await t1.target.transition_to(tool_state=get_available_flights) # Present flight options # We will transition conditionally from here based on the customer's response t3 = await t2.target.transition_to( chat_state="Present available flights and ask which one works for them" ) # We'll start with the happy path where the customer picks a flight t4 = await t3.target.transition_to( chat_state="Collect passenger information and confirm booking details before proceeding", condition="The customer selects a flight", ) t5 = await t4.target.transition_to( tool_state=book_flight, condition="The customer confirms the booking details", ) t6 = await t5.target.transition_to(chat_state="Provide confirmation number and booking summary") await t6.target.transition_to(state=p.END_JOURNEY) # Otherwise, if none of the flights work, offer alternative dates t7 = await t3.target.transition_to( tool_state=get_alternative_flights, condition="None of the flights work for the customer", ) t8 = await t7.target.transition_to(chat_state="Present alternative flights and ask if any work") # Transition back to our happy-path if they pick a flight await t8.target.transition_to(state=t4.target, condition="The customer selects a flight") # Otherwise, ask them to call the office or check our website t9 = await t8.target.transition_to( chat_state="Suggest calling our office or visiting our website for more options", condition="None of the alternative flights work either", ) await t9.target.transition_to(state=p.END_JOURNEY) # Handle edge-cases deliberately with guidelines await journey.create_guideline( condition="The customer mentions they need to travel urgently or it's an emergency", action="Direct them to call our office immediately for priority booking assistance", ) await journey.create_guideline( condition="The customer asks about visa requirements", action="Inform them that visa requirements vary by destination and nationality, and suggest they check with the embassy or consulate", ) return journey async def create_booking_status_journey(server: p.Server, agent: p.Agent) -> p.Journey: # Create the journey journey = await agent.create_journey( title="Check Booking Status", description="Retrieves the customer's booking status and provides relevant information.", conditions=["The customer wants to check their booking status"], ) t0 = await journey.initial_state.transition_to( chat_state="Ask for the confirmation number or booking reference" ) t1 = await t0.target.transition_to(tool_state=get_booking_status) await t1.target.transition_to( chat_state="Tell the customer that the booking could not be found and ask them to verify the confirmation number or call the office", condition="The booking could not be found", ) await t1.target.transition_to( chat_state="Provide the booking details and confirm everything is in order", condition="The booking is confirmed and all details are correct", ) await t1.target.transition_to( chat_state="Present the booking information and mention any issues or pending actions required", condition="The booking has issues or requires customer action", ) # Handle edge cases with guidelines... await journey.create_guideline( condition="The customer wants to make changes to their booking", action="Explain the change policy and direct them to call our office for assistance with modifications", ) await journey.create_guideline( condition="The customer is concerned about potential cancellation", action="Provide our cancellation policy and suggest they call the office to discuss their options", ) return journey async def configure_container(container: p.Container) -> p.Container: container[p.PerceivedPerformancePolicy] = p.VoiceOptimizedPerceivedPerformancePolicy() return container async def main() -> None: async with p.Server( configure_container=configure_container, ) as server: agent = await server.create_agent( name="Walker", description="Is a knowledgeable travel agent who helps book flights, answer travel questions, and manage reservations.", output_mode=p.OutputMode.STREAM, ) await add_domain_glossary(agent) await create_flight_booking_journey(server, agent) await create_booking_status_journey(server, agent) await agent.create_guideline( condition="The customer asks about travel insurance", action="Explain our travel insurance options, coverage details, and pricing, then offer to add it to their booking", ) await agent.create_guideline( condition="The customer asks about hotel or car rental options", action="Inform them that we can help with complete travel packages and suggest they call our office or visit our website for hotel and car rental bookings", ) await agent.create_guideline( condition="The customer asks to speak with a human agent", action="Provide the office phone number and office hours, and offer to help them with anything else in the meantime", ) await agent.create_guideline( condition="The customer asks about destinations or activities unrelated to booking travel", action="Acknowledge their interest but explain that you specialize in travel bookings, and gently redirect to how you can help with their travel plans", ) await agent.create_guideline( condition="The customer inquires about something that has nothing to do with travel", action="Kindly tell them you cannot assist with off-topic inquiries - do not engage with their request.", ) if __name__ == "__main__": asyncio.run(main()) ================================================ FILE: llms.txt ================================================ # Parlant > Open-source AI agent framework for building customer-facing conversational agents with ensured rule compliance and enterprise-grade behavior control. Parlant is a Python framework for building **predictable, business-aligned AI agents**. Unlike prompt-based approaches, Parlant ensures agents follow behavioral rules through structured guideline matching and contextual application. Install: `pip install parlant` ## Quick Start Example ```python import parlant.sdk as p import asyncio @p.tool async def get_account_balance(context: p.ToolContext, account_id: str) -> p.ToolResult: # Your business logic here balance = 1234.56 return p.ToolResult(data={"balance": balance, "currency": "USD"}) async def main() -> None: async with p.Server() as server: agent = await server.create_agent( name="Banking Assistant", description="Helpful and professional banking support agent", ) # Add behavioral guidelines await agent.create_guideline( condition="The customer asks about their balance", action="Retrieve and clearly present their account balance", tools=[get_account_balance], ) await agent.create_guideline( condition="The customer asks about topics unrelated to banking", action="Politely decline and redirect to banking topics", ) # Server runs until shutdown - no additional code needed here. # When the process exits, the context manager handles cleanup automatically. if __name__ == "__main__": asyncio.run(main()) ``` Run with: `python your_agent.py` then open http://localhost:8800 --- ## Core Concepts ### 1. Agents AI personalities that interact with customers. Created via `server.create_agent()`. Learn more: [Agents Documentation](https://parlant.io/docs/concepts/agents) ### 2. Guidelines Natural language if-then rules that control agent behavior contextually: ```python await agent.create_guideline( condition="When this situation occurs", # The trigger action="Do this specific thing", # The response behavior tools=[optional_tool], # Tools available for this guideline ) ``` Learn more: [Guidelines Documentation](https://parlant.io/docs/concepts/customization/guidelines) ### 3. Journeys Structured multi-step interaction flows (state machines): ```python journey = await agent.create_journey( title="Order Support", description="Helps customers with order issues", conditions=["The customer has an order-related question"], ) # Chain states with transitions t0 = await journey.initial_state.transition_to(chat_state="Ask for order number") t1 = await t0.target.transition_to(tool_state=lookup_order) t2 = await t1.target.transition_to( chat_state="Present order status", condition="Order was found", ) await t2.target.transition_to(state=p.END_JOURNEY) ``` Learn more: [Journeys Documentation](https://parlant.io/docs/concepts/customization/journeys) ### 4. Tools Functions the agent can call. Always async, always return `ToolResult`: ```python @p.tool async def my_tool( context: p.ToolContext, # Always first param required_param: str, # Required parameters optional_param: int = 10, # Optional with defaults ) -> p.ToolResult: # Business logic here return p.ToolResult(data={"key": "value"}) ``` Learn more: [Tools Documentation](https://parlant.io/docs/concepts/customization/tools) ### 5. Glossary Terms Teach agents domain-specific terminology: ```python await agent.create_term( name="SKU", description="Stock Keeping Unit - unique product identifier", synonyms=["product code", "item number"], ) ``` Learn more: [Glossary Documentation](https://parlant.io/docs/concepts/customization/glossary) ### 6. Canned Responses Template responses to eliminate hallucination and control language style: ```python await agent.add_canned_response( key="greeting", content="Hello! I'm here to help with your order. How can I assist you today?", ) ``` Learn more: [Canned Responses Documentation](https://parlant.io/docs/concepts/customization/canned-responses) ### 7. Streaming Mode Agents can deliver responses in real-time chunks for a more interactive experience: ```python from parlant.sdk import MessageOutputMode agent = await server.create_agent( name="Support Agent", description="Helpful support agent", message_output_mode=MessageOutputMode.STREAMING, # Enable streaming ) ``` Output modes: - `MessageOutputMode.BLOCK` (default): Complete response delivered at once - `MessageOutputMode.STREAMING`: Response delivered in real-time chunks with token-by-token animation Streaming mode provides actual token usage information (input/output tokens) in generation metadata. --- ## Common Patterns ### Pattern: Tool with Customer Context ```python @p.tool async def get_customer_orders(context: p.ToolContext) -> p.ToolResult: # context.customer_id is automatically available orders = await db.get_orders(context.customer_id) return p.ToolResult(data=orders) ``` ### Pattern: Conditional Transitions ```python # Branch based on conditions t0 = await journey.initial_state.transition_to(tool_state=check_eligibility) # Multiple outgoing transitions from same state await t0.target.transition_to( chat_state="Approve the request", condition="Customer is eligible", ) await t0.target.transition_to( chat_state="Explain why they're not eligible", condition="Customer is not eligible", ) ``` ### Pattern: Disambiguation Handle ambiguous user intents: ```python observation = await agent.create_observation( "The customer mentions a problem but doesn't specify what kind", ) await observation.disambiguate([billing_journey, technical_support_journey]) ``` ### Pattern: Journey-Scoped Guidelines Guidelines that only apply within a specific journey: ```python await journey.create_guideline( condition="Customer seems frustrated", action="Acknowledge their frustration and offer to escalate", ) ``` --- ## Environment Variables Set your LLM provider credentials before running. Examples: - `OPENAI_API_KEY` - For OpenAI - `ANTHROPIC_API_KEY` - For Anthropic - `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT` - For Azure OpenAI Learn more: [Installation & Setup](https://parlant.io/docs/quickstart/installation) --- ## Full Example: Customer Service Agent ```python import parlant.sdk as p import asyncio # Define tools @p.tool async def lookup_order(context: p.ToolContext, order_id: str) -> p.ToolResult: # Simulated order lookup return p.ToolResult(data={ "order_id": order_id, "status": "shipped", "tracking": "1Z999AA10123456784", }) @p.tool async def request_refund(context: p.ToolContext, order_id: str, reason: str) -> p.ToolResult: return p.ToolResult(data={"refund_id": "REF-12345", "status": "processing"}) async def create_order_journey(agent: p.Agent) -> p.Journey: journey = await agent.create_journey( title="Order Support", description="Helps customers check order status or request refunds", conditions=["Customer asks about an order"], ) # Step 1: Get order number t0 = await journey.initial_state.transition_to( chat_state="Ask the customer for their order number" ) # Step 2: Look up the order t1 = await t0.target.transition_to(tool_state=lookup_order) # Step 3a: Order found - show status t2 = await t1.target.transition_to( chat_state="Present the order status and tracking information", condition="Order was found", ) # Step 3b: Order not found await t1.target.transition_to( chat_state="Apologize and ask them to verify the order number", condition="Order was not found", ) # Step 4: Check if they need anything else t3 = await t2.target.transition_to( chat_state="Ask if they need help with anything else regarding this order" ) # Step 5a: They want a refund t4 = await t3.target.transition_to( tool_state=request_refund, condition="Customer requests a refund", ) await t4.target.transition_to( chat_state="Confirm the refund has been initiated" ) # Step 5b: They're satisfied await t3.target.transition_to( state=p.END_JOURNEY, condition="Customer has no more questions", ) # Journey-specific guidelines await journey.create_guideline( condition="Customer is upset about a delayed order", action="Apologize sincerely and offer expedited shipping on their next order", ) return journey async def main() -> None: async with p.Server() as server: agent = await server.create_agent( name="Support Agent", description="Friendly and efficient customer support representative", ) # Domain knowledge await agent.create_term( name="Express Shipping", description="2-day delivery, costs $9.99", ) # Create journey await create_order_journey(agent) # Global guidelines (apply everywhere) await agent.create_guideline( condition="Customer uses profanity or is abusive", action="Calmly ask them to be respectful, or offer to end the conversation", ) await agent.create_guideline( condition="Customer asks to speak to a human", action="Provide the support phone number: 1-800-555-0123", ) # Server runs until shutdown - no additional code needed here. # When the process exits, the context manager handles cleanup automatically. if __name__ == "__main__": asyncio.run(main()) ``` --- ## Testing Framework Parlant includes a testing framework for validating agent behavior using NLP-based assertions. ### Basic Test Structure ```python from parlant.testing import Suite from parlant.testing.steps import AgentMessage, CustomerMessage suite = Suite( server_url="http://localhost:8800", agent_id="your_agent_id", ) @suite.scenario async def test_greeting() -> None: async with suite.session() as session: response = await session.send("Hello!") await response.should("be a friendly greeting or offer to help") @suite.scenario async def test_appointment_inquiry() -> None: async with suite.session() as session: response = await session.send("Can I schedule an appointment?") await response.should("acknowledge the request or ask for more details") ``` Run tests with: `parlant-test your_test_file.py` ### NLP-Based Assertions The `response.should()` method uses NLP to evaluate conditions against the full conversation context: ```python # Single condition await response.should("be polite and professional") # Multiple conditions (evaluated in parallel) await response.should([ "ask for the reason for the visit", "be polite", "not mention pricing", ]) ``` ### Multi-Turn Conversations with unfold() Test multi-turn conversations where each step builds on history: ```python @suite.scenario async def test_booking_flow() -> None: async with suite.session() as session: await session.unfold([ # History-only steps (no assertion) CustomerMessage("Hello"), AgentMessage("Hi! How can I help you today?"), # Steps with assertions create sub-tests CustomerMessage("I need to book an appointment"), AgentMessage( text="What's the reason for your visit?", should=["ask for the reason", "be polite"], ), CustomerMessage("Regular checkup"), AgentMessage( text="I have Monday at 10am or Wednesday at 2pm available.", should="offer appointment times", ), ]) ``` **How unfold() works:** - `CustomerMessage(text)` - Customer's message in the conversation - `AgentMessage(text, should)` - Expected agent response - `text`: Reference response used as history for subsequent tests - `should`: Assertion condition(s). Only steps with `should` create sub-tests - Each sub-test gets a fresh session with prefab history of all prior steps - Sub-tests run sequentially and report results independently ### Repeated Scenarios Run the same scenario multiple times for consistency testing: ```python @suite.scenario(repetitions=3) async def test_consistent_greeting() -> None: async with suite.session() as session: response = await session.send("Hello") await response.should("greet the customer") ``` ### Hooks ```python @suite.before_all async def setup() -> None: # Runs once before all tests suite.context["api_key"] = "test-key" @suite.after_all async def teardown() -> None: # Runs once after all tests pass @suite.before_each async def before_test(test_name: str) -> None: # Runs before each test pass @suite.after_each async def after_test(test_name: str, passed: bool, error: str | None) -> None: # Runs after each test pass ``` ### CLI Options ```bash # Run all tests parlant-test tests.py # Filter by pattern parlant-test tests.py -k "greeting" # Run tests in parallel parlant-test tests.py --parallel # Custom timeout (seconds) parlant-test tests.py --timeout 120 ``` --- ## Links - Documentation: https://parlant.io/ - GitHub: https://github.com/emcie-co/parlant - PyPI: https://pypi.org/project/parlant/ - Discord: https://discord.gg/duxWqxKk6J ================================================ FILE: mypy.ini ================================================ [mypy] strict = True namespace_packages = True explicit_package_bases = True warn_unused_ignores = False mypy_path = src files = src, tests disable_error_code = type-abstract exclude = scripts plugins = pydantic.mypy ================================================ FILE: pyproject.toml ================================================ [project] name = "parlant" version = "3.3.0" description = "" readme = "README.md" license = "Apache-2.0" authors = [ {name = "Yam Marcovitz", email = "yam@emcie.co"}, {name = "Dor Zohar", email = "dor@emcie.co"}, ] requires-python = ">=3.10,<3.15" # Restricted for torch 2.8+ compatibility with triton dependencies = [ "aiofiles>=24.1.0", "aiopenapi3==0.9.0", "aiorwlock>=1.5.0", "authlib>1.6.5", "boto3>=1.35.70", "cachetools>=6.0.0", "click>=8.1.7", "colorama>=0.4.6", "coloredlogs>=15.0.1", "contextvars>=2.4", "croniter>=5.0.1", "fastapi>=0.120.0", "fastmcp>=2.14.0", "httpx>=0.28.1", "jinja2>=3.1.6", "jsonfinder>=0.4.2", "jsonschema>=4.23.0", "lagom>=2.6.0", "limits>=5.5.0", "mcp>=1.23.0", "more-itertools>=10.3.0", "nano-vectordb>=0.0.4.3", "nanoid>=2.0.0", "networkx[default]>=3.3", "openai>=2.8.0", "openapi3-parser==1.1.21", "opentelemetry-api>=1.37.0", "opentelemetry-exporter-otlp>=1.37.0", "opentelemetry-instrumentation>=0.58b0", "opentelemetry-sdk>=1.37.0", "parlant-client @ git+https://github.com/emcie-co/parlant-client-python.git@v3.2.0", "python-dateutil>=2.8.2", "python-dotenv>=1.0.1", "requests>=2.32.5", "rich>=14.0.0", "semver>=3.0.2", "starlette>=0.49.0", # Specified for fix vulnerability of lower versions. Can be removed in case of future conflicts. "structlog>=24.4.0", "tabulate>=0.9.0", "tiktoken>=0.12", "tokenizers>=0.21", "toml>=0.10.2", "types-aiofiles>=24.1.0.20240626", "types-cachetools>=6.0.0.20250525", "types-croniter>=4.0.0.20241030", "types-jsonschema>=4.22.0.20240610", "uvicorn>=0.38.0", "websocket-client>=1.5.3", "wsproto>=1.2.0", ] [project.scripts] parlant = "parlant.bin.client:main" parlant-server = "parlant.bin.server:main" parlant-prepare-migration = "parlant.bin.prepare_migration:main" [project.optional-dependencies] chroma = ["chromadb>=1.1.1"] qdrant = ["qdrant-client>=1.7.0"] mongo = ["pymongo>=4.11.1"] anthropic = ["anthropic>=0.60.0", "torch>=2.8.0", "transformers>=4.53.0"] aws = ["anthropic>=0.60.0", "transformers>=4.53.0", "torch>=2.8.0"] together = ["torch>=2.8.0", "together>=1.5.26", "transformers>=4.53.0"] cerebras = ["cerebras-cloud-sdk>=1.25.0", "torch>=2.8.0", "transformers>=4.53.0"] deepseek = ["torch>=2.8.0", "transformers>=4.53.0"] gemini = ["google-genai>=1.36.0", "google-api-core>=2.24.2", "torch>=2.8.0"] vertex = [ "google-genai>=1.36.0", "google-api-core>=2.24.2", "google-auth>=2.40.0", "torch>=2.8.0", "anthropic>=0.60.0", "transformers>=4.53.0", ] ollama = ["ollama>=0.5.0"] litellm = ["litellm>=1.61.16", "torch>=2.8.0", "transformers>=4.53.0"] azure = ["azure-identity>=1.20.0"] # fireworks = ["fireworks-ai>=0.19.19"] # Disabled: pins protobuf=5.29.3 which has CVE-2025-4565 mistral = ["mistralai>=1.0.0"] snowflake = ["snowflake-connector-python>=3.12.0"] zhipu = ["zhipuai>=2.0.0"] [dependency-groups] dev = [ "ipython>=8.26.0", "mypy>=1.18.1", "pep8-naming>=0.13.3", "pytest>=8.0.0", "pytest-asyncio>=0.23.5", "pytest-bdd>=8.1.0", "pytest-cov>=5.0.0", "pytest-tap>=3.4", "pytest-timing @ git+https://github.com/emcie-co/mc-spitfyre.git@timing_v0.1.4#subdirectory=pytest-timing", "python-dotenv>=1.0.1", "ruff>=0.9.1", "types-python-dateutil>=2.8.19.20240106", "types-requests>=2.32.0.20240712", "pytest-xdist>=3.6.1", ] [tool.uv] override-dependencies = [ "pyjwt>=2.10.1", # Override zhipuai's pyjwt<2.9.0 cap to allow mcp>=1.23.0 (CVE-2025-66416) "onnxruntime<1.24; python_full_version < '3.11'", # 1.24+ dropped Python 3.10 support ] constraint-dependencies = [ # Minimum safe versions for transitive dependencies with known CVEs. # These constraints only affect resolution — they do not force installation. "aiohttp>=3.13.3", "azure-core>=1.38.0", "cryptography>=46.0.5", "filelock>=3.20.3", "fonttools>=4.60.2", "orjson>=3.11.5", "pillow>=12.1.1", "protobuf>=6.33.5", "pyasn1>=0.6.2", "python-multipart>=0.0.22", "urllib3>=2.6.3", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.metadata] allow-direct-references = true [tool.hatch.build.targets.wheel] packages = ["src/parlant"] ================================================ FILE: pytest.ini ================================================ [pytest] asyncio_mode = auto bdd_features_base_dir = tests/ filterwarnings = ignore::pytest.PytestDeprecationWarning:pytest_bdd.* ignore::pytest.PytestWarning:.*usefixtures.*has no effect addopts = "--import-mode=importlib" markers = engine: marks tests related to engine behavior evaluation: marks tests related to preprocessing evaluations ================================================ FILE: pytest_stochastics.json ================================================ { "test_plan_list": [ { "plan": "complete", "_comment": "utility plan equivalent to running all the other plans. used for vscode test explorer.", "policy_tests": [ { "policy": "default", "tests": [ "tests/(?!core)" ] }, { "policy": "strict3", "tests": [ "tests/core/stable" ] }, { "policy": "majority3", "tests": [ "tests/core/unstable" ] } ] }, { "plan": "default_disabled", "_comment": "baseline plan to disbale all tests that are not specifically enabled by using it as a base for other plans", "policy_tests": [ { "policy": "disable", "tests": [ "tests/" ] } ] }, { "plan": "deterministic", "_comment": "Plan to test all the non-stochastic tests. All should pass.", "policy_tests": [ { "policy": "default", "tests": [ "tests/(?!core)" ] } ] }, { "plan": "core_stable", "_comment": "Stable stochastic tests should pass 3 out of 3 runs.", "policy_tests": [ { "policy": "strict3", "tests": [ "tests/core/stable" ] } ] }, { "plan": "core_unstable", "_comment": "Unstable test are allowed one failure out of 3 runs. (Temporarily encompases all of `core`)", "policy_tests": [ { "policy": "majority3", "tests": [ "tests/core/unstable" ] } ] } ], "policy_list": [ { "policy": "disable", "at_least": 0, "out_of": 0 }, { "policy": "default", "at_least": 1, "out_of": 1 }, { "policy": "experimental", "at_least": 0, "out_of": 3, "pass_fast": false }, { "policy": "majority3", "at_least": 2, "out_of": 3 }, { "policy": "strict3", "at_least": 3, "out_of": 3 } ], "plan_fallback_list": [ { "plan": "deterministic", "overrides": "default_disabled" }, { "plan": "core_stable", "overrides": "default_disabled" }, { "plan": "core_unstable", "overrides": "default_disabled" } ] } ================================================ FILE: ruff.toml ================================================ # Exclude a variety of commonly ignored directories. exclude = [ ".bzr", ".direnv", ".eggs", ".git", ".git-rewrite", ".hg", ".ipynb_checkpoints", ".mypy_cache", ".nox", ".pants.d", ".pyenv", ".pytest_cache", ".pytype", ".ruff_cache", ".svn", ".tox", ".venv", ".vscode", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "site-packages", "venv", ] # Same as Black. line-length = 100 indent-width = 4 # Assume Python 3.8 target-version = "py312" [lint] # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. select = [ "E4", "E7", "E9", "F", "W293", "PLR1722", "PTH204", "PLW2901", #"ASYNC230", TODO: don't use sync open() in async methods #"PTH123", TODO: prefer Path.open() over open() #"UP035", TODO: preferred import sources #"UP017", TODO: prefer datetimc.UTC #"I001", TODO: sort imports ] ignore = ["E203"] # Allow fix for all enabled rules (when `--fix`) is provided. fixable = ["ALL"] unfixable = [] # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" [format] # Like Black, use double quotes for strings. quote-style = "double" # Like Black, indent with spaces, rather than tabs. indent-style = "space" # Like Black, respect magic trailing commas. skip-magic-trailing-comma = false # Like Black, automatically detect the appropriate line ending. line-ending = "auto" ================================================ FILE: scripts/ci/github_action_ubuntu_2404_free_space.sh ================================================ #!/bin/sh # Print initial disk space usage df -h / | awk 'NR==2 {printf "Before cleanup: %s used, %s free\n", $3, $4}' # Remove docker images sudo docker rmi $(docker image ls -aq) >/dev/null 2>&1 || true # Remove development toolchains and SDK directories sudo rm -rf \ /opt/hostedtoolcache/* \ /usr/local/lib/android \ /usr/share/dotnet \ /usr/local/share/powershell \ /usr/share/swift \ /opt/ghc \ /usr/local/.ghcup \ /usr/lib/jvm \ /usr/local/julia* \ /usr/local/n \ /usr/local/share/chromium \ /usr/local/share/vcpkg \ >/dev/null 2>&1 || true # Remove unnecessary packages sudo apt-get remove -y \ azure-cli \ google-cloud-sdk \ firefox \ google-chrome-stable \ microsoft-edge-stable \ mysql* \ mongodb-org* \ dotnet* \ php* \ >/dev/null 2>&1 || true # Clean up package system sudo apt-get autoremove -y >/dev/null 2>&1 sudo apt-get clean -y >/dev/null 2>&1 # Clean up package caches and data sudo rm -rf \ /var/lib/docker/* \ /var/lib/gems/* \ /var/lib/apt/lists/* \ /var/cache/* \ /var/lib/snapd \ >/dev/null 2>&1 || true # Print final disk space usage and difference df -h / | awk -v before="$(df -h / | awk 'NR==2 {print $3}')" \ 'NR==2 {printf "After cleanup: %s used, %s free (freed %s)\n", $3, $4, substr(before,1,length(before)-1) - substr($3,1,length($3)-1) "G"}' ================================================ FILE: scripts/fern/docs.yml ================================================ instances: - url: https://docs.parlant.io title: Parlant | Documentation navigation: - api: API Reference colors: accentPrimary: '#ffffff' background: '#000000' ================================================ FILE: scripts/fern/fern.config.json ================================================ { "organization": "parlant", "version": "0.61.22" } ================================================ FILE: scripts/fern/generators.yml ================================================ api: specs: - openapi: openapi/parlant.openapi.json default-group: local groups: local: generators: - name: fernapi/fern-typescript-node-sdk version: 0.49.2 config: namespaceExport: Parlant output: location: local-file-system path: ../sdks/typescript - name: fernapi/fern-python-sdk version: 4.3.3 config: client_class_name: ParlantClient output: location: local-file-system path: ../sdks/python ================================================ FILE: scripts/generate_client_sdk.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #!python import os from pathlib import Path import re import subprocess import shutil import sys import time DIR_SCRIPT_ROOT = Path(__file__).parent DIR_FERN = DIR_SCRIPT_ROOT / "fern" DIR_SDKS = DIR_SCRIPT_ROOT / "sdks" DIR_PROJECTS_WORKSPACE = DIR_SCRIPT_ROOT / ".." / ".." / "parlant-sdks" PATHDICT_SDK_REPO_TARGETS = { "python": DIR_PROJECTS_WORKSPACE / "parlant-client-python" / "src" / "parlant" / "client", "typescript": DIR_PROJECTS_WORKSPACE / "parlant-client-typescript" / "src", } def replace_in_files(rootdir: Path, search: str, replace: str) -> None: rewrites: dict[str, str] = {} for subdir, _dirs, files in os.walk(rootdir): for file in files: file_path = os.path.join(subdir, file) with open(file_path, "r") as current_file: current_file_content = current_file.read() if "from parlant import" not in current_file_content: continue current_file_content = re.sub(search, replace, current_file_content) rewrites[file_path] = current_file_content for path, content in rewrites.items(): with open(path, "w") as current_file: current_file.write(content) if __name__ == "__main__": DEFAULT_PORT = 8800 port = DEFAULT_PORT if len(sys.argv) >= 2: port = int(sys.argv[1]) print(f"The script will now try to fetch the latest openapi.json from http://localhost:{port}.") input( f"Ensure that parlant-server is running on port {port} and then press any key to continue..." ) output_openapi_json = DIR_FERN / "openapi/parlant.openapi.json" output_openapi_json.parent.mkdir(exist_ok=True) output_openapi_json.touch() status, output = subprocess.getstatusoutput( f"curl -m 3 -o {output_openapi_json} http://localhost:{port}/openapi.json" ) if status != 0: print(f"Failed to fetch openapi.json from http://localhost:{port}", file=sys.stderr) print("Please ensure that the desired Parlant server is accessible there.", file=sys.stderr) sys.exit(1) for sdk, repo in PATHDICT_SDK_REPO_TARGETS.items(): if os.path.isdir(repo): continue raise Exception(f"Missing dir for {sdk}: {repo}") print(f"Fetched openapi.json from http://localhost:{port}.") if not DIR_FERN.is_dir(): raise Exception("fern directory not found where expected") for sdk in PATHDICT_SDK_REPO_TARGETS: sdk_path = DIR_SDKS / sdk if not sdk_path.is_dir(): continue print(f"Deleting old {sdk} sdk") print(f"> rm -rf {sdk_path}") shutil.rmtree(sdk_path) os.chdir(DIR_SCRIPT_ROOT) print("Invoking fern generation") print("> fern generate --log-level=debug") exit_code, generate_output = subprocess.getstatusoutput("fern generate --log-level=debug") with open("fern.generate.log", "w") as fern_log: fern_log.write(generate_output) if exit_code != os.EX_OK: raise Exception(generate_output) print("Renaming `parlant` to `parlant.client` in python imports") replace_in_files(DIR_SDKS / "python", "from parlant import", "from parlant.client import") print("touching python typing") print(f"> touch {DIR_SDKS}/python/py.typed") open(DIR_SDKS / "python/py.typed", "w") for sdk, repo in PATHDICT_SDK_REPO_TARGETS.items(): print(f"!DANGER! Deleting local `{repo}` directory and all of its contents!") time.sleep(3) print(f"> rm -rf {repo}") shutil.rmtree(repo) for sdk, repo in PATHDICT_SDK_REPO_TARGETS.items(): print(f"copying newly generated {sdk} files to {repo}") print(f"> cp -rp {DIR_SDKS}/{sdk} {repo}") shutil.copytree(DIR_SDKS / sdk, repo) ================================================ FILE: scripts/initialize_repo.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import subprocess from pathlib import Path SCRIPTS_DIR = Path("./scripts") def install_packages() -> None: subprocess.run(["python", SCRIPTS_DIR / "install_packages.py"]) def install_hooks() -> None: subprocess.run(["git", "config", "core.hooksPath", ".githooks"], check=True) if __name__ == "__main__": install_packages() install_hooks() ================================================ FILE: scripts/install_packages.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import subprocess import sys from utils import Package, die, for_each_package def install_package(package: Package) -> None: if not package.uses_uv: print(f"Skipping {package.path}...") return print(f"Installing {package.path}...") status, output = subprocess.getstatusoutput(f"uv sync --all-extras --directory {package.path}") if status != 0: print(output, file=sys.stderr) die(f"error: failed to install package: {package.path}") if __name__ == "__main__": for_each_package(install_package) ================================================ FILE: scripts/lint.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys from functools import partial from utils import Package, die, for_each_package def run_cmd_or_die( cmd: str, description: str, package: Package, ) -> None: print(f"Running {cmd} on {package.name}...") status, output = package.run_cmd(cmd) if status != 0: print(output, file=sys.stderr) die(f"error: package '{package.path}': {description}") def lint_package(mypy: bool, ruff: bool, package: Package) -> None: if mypy: run_cmd_or_die("mypy", "Please fix MyPy lint errors", package) if ruff: run_cmd_or_die("ruff check", "Please fix Ruff lint errors", package) run_cmd_or_die("ruff format --check", "Please format files with Ruff", package) if __name__ == "__main__": mypy = "--mypy" in sys.argv ruff = "--ruff" in sys.argv for_each_package(partial(lint_package, mypy, ruff)) ================================================ FILE: scripts/publish.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #!/usr/bin/python3 import semver # type: ignore import sys import subprocess import toml # type: ignore from utils import die, for_each_package, Package, get_packages def get_server_version() -> str: server_package = next(p for p in get_packages() if p.name == "parlant") project_file = server_package.path / "pyproject.toml" pyproject = toml.load(project_file) version = str(pyproject["tool"]["poetry"]["version"]) return version def run_command(args: list[str]) -> None: cmd = " ".join(args) print(f"Running {cmd}") build_process = subprocess.Popen( args=args, stdout=sys.stdout, stderr=sys.stderr, ) status = build_process.wait() if status != 0: die(f"error: command failed: {cmd}") def publish_docker() -> None: version = get_server_version() version_info = semver.parse_version_info(version) tag_versions = [ f"{version_info.major}.{version_info.minor}.{version_info.patch}.{version_info.prerelease}", ] if not version_info.prerelease: tag_versions = [ "latest", f"{version_info.major}", f"{version_info.major}.{version_info.minor}", f"{version_info.major}.{version_info.minor}.{version_info.patch}", ] else: tag_versions = [ f"{version_info.major}.{version_info.minor}.{version_info.patch}.{version_info.prerelease}", ] platforms = [ "linux/amd64", "linux/arm64", ] for version in tag_versions: run_command( [ "docker", "buildx", "build", "--platform", ",".join(platforms), "-t", f"ghcr.io/emcie-co/parlant:{version}", "-f", "Dockerfile", "--push", ".", ] ) def publish_package(package: Package) -> None: if not package.uses_uv or not package.publish: print(f"Skipping {package.path}...") return status, output = package.run_cmd("uv build") if status != 0: print(output, file=sys.stderr) die(f"error: package '{package.path}': build failed") status, output = package.run_cmd("uv publish") if status != 0: print(output, file=sys.stderr) die(f"error: package '{package.path}': publish failed") if __name__ == "__main__": for_each_package(publish_package) publish_docker() ================================================ FILE: scripts/utils.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from dataclasses import dataclass import os from pathlib import Path import subprocess import sys from typing import Callable, NoReturn @dataclass(frozen=True) class Package: name: str path: Path uses_uv: bool cmd_prefix: str publish: bool def run_cmd(self, cmd: str) -> tuple[int, str]: print(f"Running command: {self.cmd_prefix} {cmd}") return subprocess.getstatusoutput(f"{self.cmd_prefix} {cmd}") def get_repo_root() -> Path: status, output = subprocess.getstatusoutput("git rev-parse --show-toplevel") if status != 0: print(output, file=sys.stderr) print("error: failed to get repo root", file=sys.stderr) sys.exit(1) return Path(output.strip()) def get_packages() -> list[Package]: root = get_repo_root() return [ Package( name="parlant", path=root / ".", cmd_prefix="uv run", uses_uv=True, publish=True, ), ] def for_each_package( f: Callable[[Package], None], enter_dir: bool = True, ) -> None: for package in get_packages(): original_cwd = os.getcwd() if enter_dir: print(f"Entering {package.path}...") os.chdir(package.path) try: f(package) finally: os.chdir(original_cwd) def die(message: str) -> NoReturn: print(message, file=sys.stderr) sys.exit(1) ================================================ FILE: scripts/version.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #!/usr/bin/python3 from functools import partial from pathlib import Path import semver # type: ignore import subprocess import sys import re import toml # type: ignore from utils import die, for_each_package, Package, get_packages def get_project_file(package: Package) -> Path: return package.path / "pyproject.toml" def get_current_version(package: Package) -> str: content = toml.load(get_project_file(package)) return str(content["project"]["version"]) def set_package_version(version: str, package: Package) -> None: if not package.uses_uv: print(f"Skipping {package.path}...") return current_version = get_current_version(package) print(f"Setting {package.name} from version {current_version} to version {version}") project_file = get_project_file(package) project_file_content = project_file.read_text() with open(project_file, "w") as file: project_file_content = re.sub( f'\nversion = "{current_version}"\n', f'\nversion = "{version}"\n', project_file_content, count=1, ) project_file_content = re.sub( f'\nparlant-(.+?) = "{current_version}"\n', f'\nparlant-\\1 = "{version}"\n', project_file_content, ) file.write(project_file_content) status, output = package.run_cmd("uv lock") if status != 0: print(output, file=sys.stderr) die("error: failed to re-hash uv lock file") def update_version_variable_in_code(version: str) -> None: server_package = next(p for p in get_packages() if p.name == "parlant") version_file: Path = server_package.path / "src/parlant/core/version.py" version_file_content = version_file.read_text() current_version = get_current_version(server_package) version_file_content = re.sub( f'VERSION = "{current_version}"', f'VERSION = "{version}"', version_file_content, ) version_file.write_text(version_file_content) def tag_repo(version: str) -> None: status, output = subprocess.getstatusoutput(f'git tag "v{version}"') if status != 0: print(output, file=sys.stderr) die(f"error: failed to tag repo: v{version}") def get_current_server_version() -> str: server_package = next(p for p in get_packages() if p.name == "parlant") return get_current_version(server_package) def update_version( current_version: str, major: bool, minor: bool, patch: bool, rc: bool, beta: bool, alpha: bool, ) -> str: assert sum((major, minor, patch)) <= 1, "Only one component can be bumped" assert sum((rc, beta, alpha)) <= 1, "Only one pre-release label can be used" version = semver.parse_version_info(current_version) if major: version = version.bump_major() if minor: version = version.bump_minor() if patch: version = version.bump_patch() if rc: version = version.bump_prerelease("rc") elif beta: version = version.bump_prerelease("beta") elif alpha: version = version.bump_prerelease("alpha") else: version = version.finalize_version() return str(version) def there_are_pending_git_changes() -> bool: status, _ = subprocess.getstatusoutput( "git diff --quiet && git diff --cached --quiet && git ls-files --others --exclude-standard" ) return status != 0 def commit_version(version: str) -> bool: status, _ = subprocess.getstatusoutput(f"git commit -am 'Release {version}' --no-verify") return status != 0 if __name__ == "__main__": if there_are_pending_git_changes(): die("error: version bumps must take place on a clean tree with no pending changes") current_version = get_current_server_version() major = "--major" in sys.argv minor = "--minor" in sys.argv patch = "--patch" in sys.argv rc = "--rc" in sys.argv beta = "--beta" in sys.argv alpha = "--alpha" in sys.argv new_version = update_version(current_version, major, minor, patch, rc, beta, alpha) if current_version == new_version: die("error: no component was selected to be bumped") answer = input(f"Proceed with bumping {current_version} to {new_version} [N/y]?") if answer not in "yY": die("Canceled.") update_version_variable_in_code(new_version) for_each_package(partial(set_package_version, new_version)) commit_version(new_version) tag_repo(new_version) ================================================ FILE: src/parlant/adapters/db/json_file.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import json from pathlib import Path from typing import Any, Awaitable, Callable, Mapping, Optional, Sequence, cast from typing_extensions import override, Self import aiofiles from parlant.core.persistence.common import ( Cursor, SortDirection, Where, matches_filters, ensure_is_total, ObjectId, ) from parlant.core.async_utils import ReaderWriterLock from parlant.core.persistence.document_database import ( CollectionIndex, CollectionSort, BaseDocument, DeleteResult, DocumentCollection, DocumentDatabase, FindResult, InsertResult, TDocument, UpdateResult, identity_loader, ) from parlant.core.loggers import Logger class JSONFileDocumentDatabase(DocumentDatabase): def __init__( self, logger: Logger, file_path: Path, ) -> None: self.file_path = file_path self._logger = logger self._op_counter = 0 self._lock = ReaderWriterLock() if not self.file_path.exists(): self.file_path.write_text(json.dumps({})) self._raw_data: dict[str, Any] = {} self._collections: dict[str, JSONFileDocumentCollection[BaseDocument]] = {} async def flush(self) -> None: async with self._lock.writer_lock: await self._flush_unlocked() async def __aenter__(self) -> Self: async with self._lock.reader_lock: self._raw_data = await self._load_raw_data() return self async def __aexit__( self, exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[object], ) -> bool: async with self._lock.writer_lock: await self._flush_unlocked() return False async def _load_raw_data( self, ) -> dict[str, Any]: # Return an empty JSON object if the file is empty if self.file_path.stat().st_size == 0: return {} async with aiofiles.open(self.file_path, "r", encoding="utf-8") as file: return cast(dict[str, Any], json.loads(await file.read())) async def _save_data( self, data: Mapping[str, Sequence[Mapping[str, Any]]], ) -> None: async with aiofiles.open(self.file_path, mode="w", encoding="utf-8") as file: json_string = json.dumps( { **self._raw_data, **data, }, ensure_ascii=False, indent=2, ) await file.write(json_string) async def load_documents_with_loader( self, name: str, document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], documents: Sequence[BaseDocument] | None = None, ) -> Sequence[TDocument]: data: list[TDocument] = [] failed_migrations: list[BaseDocument] = [] collection_documents = documents or self._raw_data.get(name, []) for doc in collection_documents: try: if loaded_doc := await document_loader(doc): data.append(loaded_doc) else: self._logger.warning(f'Failed to load document "{doc}"') failed_migrations.append(doc) except Exception as e: self._logger.error( f"Failed to load document '{doc}' with error: {e}. Added to failed migrations collection." ) failed_migrations.append(doc) if failed_migrations: failed_migrations_collection = await self.get_or_create_collection( "failed_migrations", BaseDocument, identity_loader ) for doc in failed_migrations: await failed_migrations_collection.insert_one(doc) return data @override async def create_collection( self, name: str, schema: type[TDocument], ) -> JSONFileDocumentCollection[TDocument]: self._collections[name] = JSONFileDocumentCollection( database=self, name=name, schema=schema, ) return cast(JSONFileDocumentCollection[TDocument], self._collections[name]) @override async def get_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> JSONFileDocumentCollection[TDocument]: if collection := self._collections.get(name): return cast(JSONFileDocumentCollection[TDocument], collection) elif name in self._raw_data: self._collections[name] = JSONFileDocumentCollection( database=self, name=name, schema=schema, data=await self.load_documents_with_loader(name, document_loader), ) return cast(JSONFileDocumentCollection[TDocument], self._collections[name]) raise ValueError(f'Collection "{name}" does not exists') @override async def get_or_create_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> JSONFileDocumentCollection[TDocument]: if collection := self._collections.get(name): return cast(JSONFileDocumentCollection[TDocument], collection) elif name in self._raw_data: self._collections[name] = JSONFileDocumentCollection( database=self, name=name, schema=schema, data=await self.load_documents_with_loader(name, document_loader), ) return cast(JSONFileDocumentCollection[TDocument], self._collections[name]) self._collections[name] = JSONFileDocumentCollection( database=self, name=name, schema=schema, data=await self.load_documents_with_loader(name, document_loader), ) return cast(JSONFileDocumentCollection[TDocument], self._collections[name]) @override async def delete_collection( self, name: str, ) -> None: if name in self._collections: del self._collections[name] return raise ValueError(f'Collection "{name}" does not exists') async def _flush_unlocked(self) -> None: data = {} for collection_name in self._collections: data[collection_name] = self._collections[collection_name].documents await self._save_data(data) class JSONFileDocumentCollection(DocumentCollection[TDocument]): def __init__( self, database: JSONFileDocumentDatabase, name: str, schema: type[TDocument], data: Sequence[TDocument] | None = None, ) -> None: self._database = database self._name = name self._schema = schema self._op_counter = 0 self._lock = ReaderWriterLock() self.documents = list(data) if data else [] @override async def find( self, filters: Where, limit: Optional[int] = None, cursor: Optional[Cursor] = None, sort_direction: Optional[SortDirection] = None, ) -> FindResult[TDocument]: async with self._lock.reader_lock: # First, filter documents filtered_docs = [doc for doc in self.documents if matches_filters(filters, doc)] # Sort by creation_utc with id as tiebreaker according to sort_direction sort_direction = sort_direction or SortDirection.ASC filtered_docs = self._apply_sort(filtered_docs, sort_direction) # Apply cursor-based pagination if cursor is provided if cursor: filtered_docs = self._apply_cursor_filter(filtered_docs, cursor, sort_direction) total_count = len(filtered_docs) # Apply limit has_more = False next_cursor = None if limit is not None and len(filtered_docs) > limit: # There are more items beyond the limit has_more = True result_docs = filtered_docs[:limit] # Generate next cursor from the last item if we have results if result_docs: last_doc = result_docs[-1] next_cursor = Cursor( creation_utc=str(last_doc.get("creation_utc", "")), id=ObjectId(str(last_doc.get("id", ""))), ) else: result_docs = filtered_docs return FindResult( items=result_docs, total_count=total_count, has_more=has_more, next_cursor=next_cursor, ) def _apply_sort( self, documents: list[TDocument], sort_direction: SortDirection, ) -> list[TDocument]: docs = list(documents) # don't mutate input # Sort by creation_utc with id as tiebreaker according to sort_direction reverse_order = sort_direction == SortDirection.DESC docs.sort( key=lambda d: ( d.get("creation_utc") or "", # Primary sort: creation_utc d.get("id") or "", # Tiebreaker: id ), reverse=reverse_order, ) return docs def _apply_field_sort( self, documents: Sequence[TDocument], sort: CollectionSort, ) -> list[TDocument]: docs = list(documents) for field_name, direction in reversed(sort): docs.sort( key=lambda d: cast(Any, d.get(field_name)), reverse=direction == SortDirection.DESC, ) return docs def _apply_cursor_filter( self, documents: list[TDocument], cursor: Cursor, sort_direction: SortDirection, ) -> list[TDocument]: result = [] for doc in documents: doc_creation_utc = str(doc.get("creation_utc", "")) doc_id = str(doc.get("id", "")) if sort_direction == SortDirection.DESC: # For descending order pagination, include documents that come after the cursor # This matches the MongoDB query pattern: # { "$or": [ # { "creation_utc": { "$lt": cursor.creation_utc } }, # { "creation_utc": cursor.creation_utc, "id": { "$lt": cursor.id } } # ]} if doc_creation_utc < cursor.creation_utc or ( doc_creation_utc == cursor.creation_utc and doc_id < cursor.id ): result.append(doc) else: # SortDirection.ASC # For ascending order pagination, include documents that come after the cursor # { "$or": [ # { "creation_utc": { "$gt": cursor_creation_utc } }, # { "creation_utc": cursor.creation_utc, "id": { "$gt": cursor.id } } # ]} if doc_creation_utc > cursor.creation_utc or ( doc_creation_utc == cursor.creation_utc and doc_id > cursor.id ): result.append(doc) return result @override async def find_one( self, filters: Where, sort: Optional[CollectionSort] = None, ) -> Optional[TDocument]: async with self._lock.reader_lock: matching_documents = [doc for doc in self.documents if matches_filters(filters, doc)] if sort: matching_documents = self._apply_field_sort(matching_documents, sort) for doc in matching_documents: return doc return None @override async def ensure_indexes( self, indexes: Sequence[CollectionIndex], ) -> None: return None @override async def insert_one( self, document: TDocument, ) -> InsertResult: ensure_is_total(document, self._schema) async with self._lock.writer_lock: self.documents.append(document) await self._database.flush() return InsertResult(acknowledged=True) @override async def update_one( self, filters: Where, params: TDocument, upsert: bool = False, ) -> UpdateResult[TDocument]: async with self._lock.writer_lock: for i, d in enumerate(self.documents): if matches_filters(filters, d): self.documents[i] = cast(TDocument, {**self.documents[i], **params}) await self._database.flush() return UpdateResult( acknowledged=True, matched_count=1, modified_count=1, updated_document=self.documents[i], ) if upsert: await self.insert_one(params) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=params, ) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=None, ) @override async def delete_one( self, filters: Where, ) -> DeleteResult[TDocument]: async with self._lock.writer_lock: for i, d in enumerate(self.documents): if matches_filters(filters, d): document = self.documents.pop(i) await self._database.flush() return DeleteResult( deleted_count=1, acknowledged=True, deleted_document=document ) return DeleteResult( acknowledged=True, deleted_count=0, deleted_document=None, ) ================================================ FILE: src/parlant/adapters/db/mongo_db.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import Any, Awaitable, Callable, Optional, Sequence from bson import CodecOptions from typing_extensions import Self from parlant.core.loggers import Logger from parlant.core.persistence.common import Cursor, SortDirection, Where, ObjectId from parlant.core.persistence.document_database import ( CollectionIndex, CollectionSort, BaseDocument, DeleteResult, DocumentCollection, DocumentDatabase, FindResult, InsertResult, TDocument, UpdateResult, ) from pymongo import AsyncMongoClient from pymongo.asynchronous.database import AsyncDatabase from pymongo.asynchronous.collection import AsyncCollection class MongoDocumentDatabase(DocumentDatabase): def __init__( self, mongo_client: AsyncMongoClient[Any], database_name: str, logger: Logger, ): self.mongo_client: AsyncMongoClient[Any] = mongo_client self.database_name = database_name self._logger = logger self._database: Optional[AsyncDatabase[Any]] = None self._collections: dict[str, MongoDocumentCollection[Any]] = {} async def create_collection( self, name: str, schema: type[TDocument], ) -> DocumentCollection[TDocument]: if self._database is None: raise Exception("underlying database missing.") collection = await self._database.create_collection( name=name, codec_options=CodecOptions(document_class=schema), ) self._collections[name] = MongoDocumentCollection(self, collection) await self._collections[name].ensure_indexes( [CollectionIndex(fields=(("creation_utc", SortDirection.ASC),))] ) return self._collections[name] async def get_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[TDocument | None]], ) -> DocumentCollection[TDocument]: if self._database is None: raise Exception("underlying database missing.") result_collection = self._database.get_collection( name=name, codec_options=CodecOptions(document_class=schema), ) failed_migrations_collection_name = f"{self.database_name}_{name}_failed_migrations" collection_existing_documents = result_collection.find({}) if failed_migrations_collection_name in await self._database.list_collection_names(): self._logger.info(f"deleting old `{failed_migrations_collection_name}` collection") await self.delete_collection(failed_migrations_collection_name) failed_migration_collection: Optional[DocumentCollection[TDocument]] = None for doc in await collection_existing_documents.to_list(): try: if loaded_doc := await document_loader(doc): await result_collection.replace_one(doc, loaded_doc) continue if failed_migration_collection is None: self._logger.warning( f"creating: `{failed_migrations_collection_name}` collection to store failed migrations..." ) failed_migration_collection = await self.create_collection( failed_migrations_collection_name, schema ) self._logger.warning(f'failed to load document "{doc}"') await failed_migration_collection.insert_one(doc) await result_collection.delete_one(doc) except Exception as e: if failed_migration_collection is None: self._logger.warning( f"creating: `{failed_migrations_collection_name}` collection to store failed migrations..." ) failed_migration_collection = await self.create_collection( failed_migrations_collection_name, schema ) self._logger.error( f"failed to load document '{doc}' with error: {e}. Added to `{failed_migrations_collection_name}` collection." ) await failed_migration_collection.insert_one(doc) self._collections[name] = MongoDocumentCollection(self, result_collection) await self._collections[name].ensure_indexes( [CollectionIndex(fields=(("creation_utc", SortDirection.ASC),))] ) return self._collections[name] async def get_or_create_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[TDocument | None]], ) -> DocumentCollection[TDocument]: return await self.get_collection(name, schema, document_loader) async def delete_collection(self, name: str) -> None: if self._database is None: raise Exception("underlying database missing.") await self._database.drop_collection(name) async def __aenter__(self) -> Self: self._database = self.mongo_client[self.database_name] return self async def __aexit__( self, exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[object], ) -> bool: if self._database is not None: self._database = None return False class MongoDocumentCollection(DocumentCollection[TDocument]): def __init__( self, mongo_document_database: MongoDocumentDatabase, mongo_collection: AsyncCollection[TDocument], ) -> None: self._database = mongo_document_database self._collection = mongo_collection async def find( self, filters: Where, limit: Optional[int] = None, cursor: Optional[Cursor] = None, sort_direction: Optional[SortDirection] = None, ) -> FindResult[TDocument]: query = dict(filters) if filters else {} sort_direction = sort_direction or SortDirection.ASC if cursor is not None: if sort_direction == SortDirection.DESC: cursor_conditions = [ {"creation_utc": {"$lt": cursor.creation_utc}}, { "$and": [ {"creation_utc": cursor.creation_utc}, {"id": {"$lt": cursor.id}}, ] }, ] else: cursor_conditions = [ {"creation_utc": {"$gt": cursor.creation_utc}}, { "$and": [ {"creation_utc": cursor.creation_utc}, {"id": {"$gt": cursor.id}}, ] }, ] query["$or"] = cursor_conditions # Sort by creation_utc with id as tiebreaker according to sort_direction sort_order = -1 if sort_direction == SortDirection.DESC else 1 sort_spec = [("creation_utc", sort_order), ("id", sort_order)] # Get one extra document to check if there are more query_limit = (limit + 1) if limit else None mongo_cursor = self._collection.find(query).sort(sort_spec) if query_limit: mongo_cursor = mongo_cursor.limit(query_limit) items = await mongo_cursor.to_list(length=query_limit) # Calculate pagination metadata has_more = False next_cursor = None total_count = len(items) if limit and len(items) > limit: has_more = True items = items[:limit] # Remove the extra item # Create cursor from the last item if items: last_item = items[-1] next_cursor = Cursor( creation_utc=str(last_item.get("creation_utc", "")), id=ObjectId(str(last_item.get("id", ""))), ) return FindResult( items=items, total_count=total_count, has_more=has_more, next_cursor=next_cursor ) def _translate_sort( self, sort: CollectionSort, ) -> list[tuple[str, int]]: return [ (field_name, -1 if direction == SortDirection.DESC else 1) for field_name, direction in sort ] async def find_one( self, filters: Where, sort: Optional[CollectionSort] = None, ) -> TDocument | None: mongo_sort = self._translate_sort(sort) if sort else None result = await self._collection.find_one(filters, sort=mongo_sort) return result async def ensure_indexes( self, indexes: Sequence[CollectionIndex], ) -> None: for index in indexes: await self._collection.create_index( self._translate_sort(index.fields), unique=index.unique, ) async def insert_one(self, document: TDocument) -> InsertResult: insert_result = await self._collection.insert_one(document) return InsertResult(acknowledged=insert_result.acknowledged) async def update_one( self, filters: Where, params: TDocument, upsert: bool = False, ) -> UpdateResult[TDocument]: update_result = await self._collection.update_one(filters, {"$set": params}, upsert) result_document = await self._collection.find_one(filters) return UpdateResult[TDocument]( update_result.acknowledged, update_result.matched_count, update_result.modified_count, result_document, ) async def delete_one(self, filters: Where) -> DeleteResult[TDocument]: result_document = await self._collection.find_one(filters) if result_document is None: return DeleteResult(True, 0, None) delete_result = await self._collection.delete_one(filters) return DeleteResult( delete_result.acknowledged, deleted_count=delete_result.deleted_count, deleted_document=result_document, ) ================================================ FILE: src/parlant/adapters/db/snowflake_db.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Maintainer: Tao Tang from __future__ import annotations import asyncio import importlib import json import os import re from typing import ( Any, Awaitable, Callable, Literal, Mapping, MutableMapping, Optional, Sequence, cast, ) from typing_extensions import Self from parlant.core.loggers import Logger from parlant.core.persistence.common import Cursor, ObjectId, SortDirection, Where, ensure_is_total from parlant.core.persistence.document_database import ( CollectionIndex, CollectionSort, BaseDocument, DeleteResult, DocumentCollection, DocumentDatabase, FindResult, InsertResult, TDocument, UpdateResult, ) class SnowflakeAdapterError(Exception): """Raised for recoverable adapter errors.""" _IDENTIFIER_RE = re.compile(r"[^0-9A-Za-z_]") def _sanitize_identifier(raw: str) -> str: sanitized = _IDENTIFIER_RE.sub("_", raw).upper() if not sanitized: raise SnowflakeAdapterError("Snowflake identifier cannot be empty") if sanitized[0].isdigit(): return f"_{sanitized}" return sanitized def _stringify(value: Any) -> Optional[str]: if value is None: return None object_id_type = getattr(ObjectId, "__supertype__", str) if isinstance(value, object_id_type): return str(value) return str(value) def _load_connection_params_from_env() -> dict[str, Any]: env = os.environ required = [ "SNOWFLAKE_ACCOUNT", "SNOWFLAKE_USER", "SNOWFLAKE_WAREHOUSE", "SNOWFLAKE_DATABASE", "SNOWFLAKE_SCHEMA", ] missing = [key for key in required if not env.get(key)] if missing: raise SnowflakeAdapterError( "Missing Snowflake configuration. Set the following environment variables: " + ", ".join(missing) ) params: dict[str, Any] = { "account": env["SNOWFLAKE_ACCOUNT"], "user": env["SNOWFLAKE_USER"], "warehouse": env["SNOWFLAKE_WAREHOUSE"], "database": env["SNOWFLAKE_DATABASE"], "schema": env["SNOWFLAKE_SCHEMA"], } if env.get("SNOWFLAKE_ROLE"): params["role"] = env["SNOWFLAKE_ROLE"] token = env.get("SNOWFLAKE_TOKEN") password = env.get("SNOWFLAKE_PASSWORD") if token: params["authenticator"] = "oauth" params["token"] = token elif password: params["authenticator"] = env.get("SNOWFLAKE_AUTHENTICATOR", "snowflake") params["password"] = password else: raise SnowflakeAdapterError( "Provide either SNOWFLAKE_PASSWORD or SNOWFLAKE_TOKEN for authentication" ) return params FetchMode = Literal["none", "all", "one"] class SnowflakeDocumentDatabase(DocumentDatabase): def __init__( self, logger: Logger, connection_params: Mapping[str, Any] | None = None, *, table_prefix: str | None = None, connection_factory: Callable[[Mapping[str, Any]], Any] | None = None, ) -> None: self._logger = logger self._connection_params = ( dict(connection_params) if connection_params is not None else _load_connection_params_from_env() ) self._table_prefix = _sanitize_identifier(table_prefix) if table_prefix else "PARLANT_" self._connection_factory = connection_factory self._connector_module: Any | None = None self._snowflake_error: type[BaseException] | None = None self._dict_cursor_cls: Any | None = None self._connection: Any | None = None self._collections: dict[str, SnowflakeDocumentCollection[Any]] = {} self._initialized: set[str] = set() self._init_locks: dict[str, asyncio.Lock] = {} self._connection_lock = asyncio.Lock() self._operation_lock = asyncio.Lock() async def __aenter__(self) -> Self: await self._ensure_connection() return self async def __aexit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: object | None, ) -> bool: if self._connection is not None: await asyncio.to_thread(self._connection.close) self._connection = None return False async def create_collection( self, name: str, schema: type[TDocument], ) -> SnowflakeDocumentCollection[TDocument]: return await self._get_or_create_initialized_collection( name, schema, document_loader=None, ) async def get_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> SnowflakeDocumentCollection[TDocument]: return await self._get_or_create_initialized_collection( name, schema, document_loader=document_loader, ) async def get_or_create_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> SnowflakeDocumentCollection[TDocument]: return await self.get_collection(name, schema, document_loader) async def delete_collection(self, name: str) -> None: table = self._table_identifier(name) failed_table = self._failed_table_identifier(name) await self._execute(f"DROP TABLE IF EXISTS {table}") await self._execute(f"DROP TABLE IF EXISTS {failed_table}") self._collections.pop(name, None) async def _get_or_create_initialized_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]] | None, ) -> SnowflakeDocumentCollection[TDocument]: if name not in self._collections: self._collections[name] = SnowflakeDocumentCollection( database=self, name=name, schema=schema, logger=self._logger, ) collection = cast(SnowflakeDocumentCollection[TDocument], self._collections[name]) if name in self._initialized: return collection lock = self._init_locks.setdefault(name, asyncio.Lock()) async with lock: if name in self._initialized: return collection create_stmt = f""" CREATE TABLE IF NOT EXISTS {collection._table} ( ID STRING NOT NULL, VERSION STRING, CREATION_UTC STRING, DATA VARIANT, PRIMARY KEY (ID) ) """ await self._execute(create_stmt) await self._execute( f""" CREATE TABLE IF NOT EXISTS {collection._failed_table} ( ID STRING, DATA VARIANT ) """ ) if document_loader is not None: await self.load_documents_with_loader(collection, document_loader) self._initialized.add(name) return collection async def load_documents_with_loader( self, collection: SnowflakeDocumentCollection[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> None: rows = await self._execute( f"SELECT DATA FROM {collection._table}", fetch="all", ) failed: list[BaseDocument] = [] for row in rows or []: doc = collection._row_to_document(row) try: migrated = await document_loader(doc) except Exception as exc: # pragma: no cover self._logger.error( f"Failed to load document '{doc.get('id')}' in collection '{collection._name}': {exc}" ) failed.append(doc) continue if migrated is None: failed.append(doc) continue if migrated is not doc: await collection._replace_document(migrated) if failed: await collection._persist_failed_documents(failed) await collection._delete_documents([doc["id"] for doc in failed if "id" in doc]) async def _execute( self, sql: str, params: Mapping[str, Any] | Sequence[Any] | None = None, *, fetch: FetchMode = "none", ) -> Any: await self._ensure_connection() async with self._operation_lock: return await asyncio.to_thread(self._run_query, sql, params, fetch) def _run_query( self, sql: str, params: Mapping[str, Any] | Sequence[Any] | None, fetch: FetchMode, ) -> Any: assert self._connection is not None cursor = ( self._connection.cursor(self._dict_cursor_cls) if self._dict_cursor_cls is not None else self._connection.cursor() ) try: cursor.execute(sql, params) if fetch == "all": return cursor.fetchall() if fetch == "one": return cursor.fetchone() return None except Exception as exc: # pragma: no cover - wrapped below if self._snowflake_error and isinstance(exc, self._snowflake_error): raise SnowflakeAdapterError(f"Snowflake query failed: {exc}") from exc raise finally: cursor.close() async def _ensure_connection(self) -> None: if self._connection is not None: return async with self._connection_lock: if self._connection is not None: return self._import_connector() if self._connection_factory is not None: self._connection = self._connection_factory(self._connection_params) else: assert self._connector_module is not None self._connection = await asyncio.to_thread( self._connector_module.connect, **self._connection_params, ) def _import_connector(self) -> None: if self._connector_module is not None: return try: connector_module = importlib.import_module("snowflake.connector") except ImportError as exc: # pragma: no cover - exercised when dependency missing raise SnowflakeAdapterError( "Snowflake adapter requires snowflake-connector-python. Install parlant[snowflake]." ) from exc self._connector_module = connector_module self._dict_cursor_cls = getattr(connector_module, "DictCursor", None) try: errors_module = importlib.import_module("snowflake.connector.errors") self._snowflake_error = getattr(errors_module, "Error", None) except ImportError: self._snowflake_error = None def _table_identifier(self, name: str) -> str: return f'"{_sanitize_identifier(self._table_prefix + name)}"' def _failed_table_identifier(self, name: str) -> str: return f'"{_sanitize_identifier(self._table_prefix + name + "_failed_migrations")}"' class SnowflakeDocumentCollection(DocumentCollection[TDocument]): INDEXED_FIELDS = { "id", "version", "creation_utc", } def __init__( self, database: SnowflakeDocumentDatabase, name: str, schema: type[TDocument], logger: Logger, ) -> None: self._database = database self._name = name self._schema = schema self._logger = logger self._table = self._database._table_identifier(name) self._failed_table = self._database._failed_table_identifier(name) async def find( self, filters: Where, limit: Optional[int] = None, cursor: Optional[Cursor] = None, sort_direction: Optional[SortDirection] = None, ) -> FindResult[TDocument]: sort_direction = sort_direction or SortDirection.ASC base_clause, base_params = _build_where_clause(filters, self.INDEXED_FIELDS) params: dict[str, Any] = dict(base_params) cursor_clause, cursor_params = _build_cursor_clause(cursor, sort_direction) clause = base_clause if cursor_clause: clause = f"{clause} AND {cursor_clause}" if clause else f"WHERE {cursor_clause}" params.update(cursor_params) order_direction = "DESC" if sort_direction == SortDirection.DESC else "ASC" order_by = f"ORDER BY CREATION_UTC {order_direction}, ID {order_direction}" query_limit = (limit + 1) if limit else None limit_sql = f" LIMIT {query_limit}" if query_limit else "" sql = f"SELECT DATA FROM {self._table}" if clause: sql += f" {clause}" sql += f" {order_by}{limit_sql}" rows = await self._database._execute(sql, params or None, fetch="all") documents = [cast(TDocument, self._row_to_document(row)) for row in rows or []] total_count = len(documents) has_more = False next_cursor = None if limit and len(documents) > limit: has_more = True documents = documents[:limit] if documents: last_doc = documents[-1] creation_utc = last_doc.get("creation_utc") identifier = last_doc.get("id") if creation_utc is not None and identifier is not None: next_cursor = Cursor( creation_utc=str(creation_utc), id=ObjectId(str(identifier)), ) return FindResult( items=documents, total_count=total_count, has_more=has_more, next_cursor=next_cursor, ) def _apply_field_sort( self, documents: Sequence[TDocument], sort: CollectionSort, ) -> list[TDocument]: docs = list(documents) for field_name, direction in reversed(sort): docs.sort( key=lambda d: cast(Any, d.get(field_name)), reverse=direction == SortDirection.DESC, ) return docs async def find_one( self, filters: Where, sort: Optional[CollectionSort] = None, ) -> Optional[TDocument]: if sort: matching_documents = list((await self.find(filters=filters)).items) sorted_documents = self._apply_field_sort(matching_documents, sort) return sorted_documents[0] if sorted_documents else None clause, params = _build_where_clause(filters, self.INDEXED_FIELDS) sql = f"SELECT DATA FROM {self._table} {clause} LIMIT 1" row = await self._database._execute(sql, params, fetch="one") if not row: return None return cast(TDocument, self._row_to_document(row)) async def ensure_indexes( self, indexes: Sequence[CollectionIndex], ) -> None: return None async def insert_one(self, document: TDocument) -> InsertResult: ensure_is_total(document, self._schema) params = self._serialize_document(document) sql = f""" INSERT INTO {self._table} (ID, VERSION, CREATION_UTC, DATA) SELECT V.ID, V.VERSION, V.CREATION_UTC, PARSE_JSON(V.DATA_RAW) FROM VALUES ( %(id)s, %(version)s, %(creation_utc)s, %(data)s ) AS V(ID, VERSION, CREATION_UTC, DATA_RAW) """ await self._database._execute(sql, params) return InsertResult(acknowledged=True) async def update_one( self, filters: Where, params: TDocument, upsert: bool = False, ) -> UpdateResult[TDocument]: existing = await self.find_one(filters) if existing: updated_document = cast(TDocument, {**existing, **params}) await self._replace_document(updated_document) return UpdateResult( True, matched_count=1, modified_count=1, updated_document=updated_document, ) if upsert: await self.insert_one(params) return UpdateResult(True, matched_count=0, modified_count=0, updated_document=params) return UpdateResult(True, matched_count=0, modified_count=0, updated_document=None) async def delete_one(self, filters: Where) -> DeleteResult[TDocument]: existing = await self.find_one(filters) if not existing: return DeleteResult(True, deleted_count=0, deleted_document=None) identifier = existing.get("id") if identifier is None: return DeleteResult(True, deleted_count=0, deleted_document=None) await self._delete_documents([identifier]) return DeleteResult(True, deleted_count=1, deleted_document=existing) def _row_to_document(self, row: Any) -> BaseDocument: if isinstance(row, Mapping): data = row.get("DATA") else: data = row[0] if isinstance(data, str): return cast(BaseDocument, json.loads(data)) return cast(BaseDocument, data) async def _replace_document(self, document: TDocument) -> None: params = self._serialize_document(document) sql = f""" UPDATE {self._table} SET VERSION=%(version)s, CREATION_UTC=%(creation_utc)s, DATA=PARSE_JSON(%(data)s) WHERE ID=%(id)s """ await self._database._execute(sql, params) async def _delete_documents(self, identifiers: Sequence[Any]) -> None: if not identifiers: return placeholders = ", ".join(f"%(id_{i})s" for i in range(len(identifiers))) params = {f"id_{i}": _stringify(value) for i, value in enumerate(identifiers)} sql = f"DELETE FROM {self._table} WHERE ID IN ({placeholders})" await self._database._execute(sql, params) async def _persist_failed_documents(self, documents: Sequence[BaseDocument]) -> None: if not documents: return for doc in documents: params = { "id": _stringify(doc.get("id")), "data": json.dumps(doc, ensure_ascii=False), } sql = f""" INSERT INTO {self._failed_table} (ID, DATA) SELECT V.ID, PARSE_JSON(V.DATA_RAW) FROM VALUES (%(id)s, %(data)s) AS V(ID, DATA_RAW) """ await self._database._execute(sql, params) def _serialize_document(self, document: TDocument) -> MutableMapping[str, Any]: return { "id": _stringify(document["id"]), "version": document.get("version"), "creation_utc": document.get("creation_utc"), "data": json.dumps(document, ensure_ascii=False), } def _build_where_clause(filters: Where, indexed_fields: set[str]) -> tuple[str, Mapping[str, Any]]: if not filters: return "", {} translator = _WhereTranslator(indexed_fields) clause = translator.render(filters) if not clause: return "", {} return f"WHERE {clause}", translator.params def _build_cursor_clause( cursor: Cursor | None, sort_direction: SortDirection, ) -> tuple[str, Mapping[str, Any]]: if cursor is None: return "", {} creation_operator = "<" if sort_direction == SortDirection.DESC else ">" id_operator = "<" if sort_direction == SortDirection.DESC else ">" clause = ( f"(CREATION_UTC {creation_operator} %(cursor_creation)s " f"OR (CREATION_UTC = %(cursor_creation)s AND ID {id_operator} %(cursor_id)s))" ) params = { "cursor_creation": cursor.creation_utc, "cursor_id": str(cursor.id), } return clause, params class _WhereTranslator: def __init__(self, indexed_fields: set[str]) -> None: self._indexed_fields = indexed_fields self._params: dict[str, Any] = {} self._counter = 0 @property def params(self) -> Mapping[str, Any]: return self._params def render(self, filters: Where) -> str: return self._render(filters) def _render(self, filters: Where) -> str: if not filters: return "" if isinstance(filters, Mapping): fragments: list[str] = [] for key, value in filters.items(): if key == "$and": parts = [self._render(part) for part in cast(Sequence[Where], value)] parts = [part for part in parts if part] if parts: fragments.append("(" + " AND ".join(parts) + ")") elif key == "$or": parts = [self._render(part) for part in cast(Sequence[Where], value)] parts = [part for part in parts if part] if parts: fragments.append("(" + " OR ".join(parts) + ")") else: fragments.append(self._render_field(key, value)) return " AND ".join(part for part in fragments if part) raise SnowflakeAdapterError("Unsupported filter format for Snowflake adapter") def _render_field(self, field: str, condition: Any) -> str: if not isinstance(condition, Mapping): return self._equality_clause(field, condition) clauses: list[str] = [] for operator, operand in condition.items(): if operator == "$eq": clauses.append(self._equality_clause(field, operand)) elif operator in {"$gt", "$gte", "$lt", "$lte", "$ne"}: clauses.append(self._comparison_clause(field, operator, operand)) elif operator == "$in": clauses.append(self._membership_clause(field, operand, negate=False)) elif operator == "$nin": clauses.append(self._membership_clause(field, operand, negate=True)) else: raise SnowflakeAdapterError( f"Unsupported operator '{operator}' in Snowflake filter" ) return " AND ".join(clauses) def _membership_clause(self, field: str, operand: Any, *, negate: bool) -> str: values = list(operand or []) if not values: return "1=1" if negate else "1=0" column, needs_variant = self._column_expr(field) placeholders: list[str] = [] for value in values: name = self._add_param(value) placeholders.append(self._wrap_value(name, needs_variant)) operator = "NOT IN" if negate else "IN" return f"{column} {operator} (" + ", ".join(placeholders) + ")" def _equality_clause(self, field: str, operand: Any) -> str: name = self._add_param(operand) column, needs_variant = self._column_expr(field) return f"{column} = {self._wrap_value(name, needs_variant)}" def _column_expr(self, field: str) -> tuple[str, bool]: sanitized = _sanitize_identifier(field) if field in self._indexed_fields: return f'"{sanitized}"', False json_path = json.dumps(field) return f"DATA:{json_path}", True def _wrap_value(self, placeholder: str, needs_variant: bool) -> str: return f"TO_VARIANT({placeholder})" if needs_variant else placeholder def _comparison_clause(self, field: str, operator: str, operand: Any) -> str: sql_operator = { "$gt": ">", "$gte": ">=", "$lt": "<", "$lte": "<=", "$ne": "!=", }[operator] name = self._add_param(operand) column, needs_variant = self._column_expr(field) return f"{column} {sql_operator} {self._wrap_value(name, needs_variant)}" def _add_param(self, value: Any) -> str: name = f"param_{self._counter}" self._counter += 1 object_id_type = getattr(ObjectId, "__supertype__", str) if isinstance(value, object_id_type): value = str(value) self._params[name] = value return f"%({name})s" __all__ = [ "SnowflakeAdapterError", "SnowflakeDocumentCollection", "SnowflakeDocumentDatabase", ] ================================================ FILE: src/parlant/adapters/db/transient.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations from typing import Any, Awaitable, Callable, Optional, Sequence, cast from typing_extensions import override from typing_extensions import get_type_hints from parlant.core.persistence.common import ( Cursor, SortDirection, matches_filters, Where, ObjectId, ensure_is_total, ) from parlant.core.persistence.document_database import ( CollectionIndex, CollectionSort, BaseDocument, DeleteResult, DocumentCollection, DocumentDatabase, FindResult, InsertResult, TDocument, UpdateResult, ) class TransientDocumentDatabase(DocumentDatabase): def __init__(self) -> None: self._collections: dict[str, TransientDocumentCollection[BaseDocument]] = {} @override async def create_collection( self, name: str, schema: type[TDocument], ) -> TransientDocumentCollection[TDocument]: annotations = get_type_hints(schema) assert "id" in annotations and annotations["id"] == ObjectId self._collections[name] = TransientDocumentCollection( name=name, schema=schema, ) return cast(TransientDocumentCollection[TDocument], self._collections[name]) @override async def get_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> TransientDocumentCollection[TDocument]: if name in self._collections: return cast(TransientDocumentCollection[TDocument], self._collections[name]) raise ValueError(f'Collection "{name}" does not exist') @override async def get_or_create_collection( self, name: str, schema: type[TDocument], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> TransientDocumentCollection[TDocument]: if collection := self._collections.get(name): return cast(TransientDocumentCollection[TDocument], collection) annotations = get_type_hints(schema) assert "id" in annotations and annotations["id"] == ObjectId return await self.create_collection( name=name, schema=schema, ) @override async def delete_collection( self, name: str, ) -> None: if name in self._collections: del self._collections[name] else: raise ValueError(f'Collection "{name}" does not exist') class TransientDocumentCollection(DocumentCollection[TDocument]): def __init__( self, name: str, schema: type[TDocument], data: Optional[Sequence[TDocument]] = None, ) -> None: self._name = name self._schema = schema self._documents = list(data) if data else [] @override async def find( self, filters: Where, limit: Optional[int] = None, cursor: Optional[Cursor] = None, sort_direction: Optional[SortDirection] = None, ) -> FindResult[TDocument]: # First, filter documents filtered_docs = [doc for doc in self._documents if matches_filters(filters, doc)] # Sort by creation_utc with id as tiebreaker according to sort_direction sort_direction = sort_direction or SortDirection.ASC filtered_docs = self._apply_sort(filtered_docs, sort_direction) # Apply cursor-based pagination if cursor is provided if cursor: filtered_docs = self._apply_cursor_filter(filtered_docs, cursor, sort_direction) total_count = len(filtered_docs) # Apply limit has_more = False next_cursor = None if limit is not None and len(filtered_docs) > limit: # There are more items beyond the limit has_more = True result_docs = filtered_docs[:limit] # Generate next cursor from the last item if we have results if result_docs: last_doc = result_docs[-1] next_cursor = Cursor( creation_utc=str(last_doc.get("creation_utc", "")), id=ObjectId(str(last_doc.get("id", ""))), ) else: result_docs = filtered_docs return FindResult( items=result_docs, total_count=total_count, has_more=has_more, next_cursor=next_cursor, ) def _apply_sort( self, documents: list[TDocument], sort_direction: SortDirection, ) -> list[TDocument]: docs = list(documents) # don't mutate input # Sort by creation_utc with id as tiebreaker according to sort_direction reverse_order = sort_direction == SortDirection.DESC docs.sort( key=lambda d: ( d.get("creation_utc") or "", # Primary sort: creation_utc d.get("id") or "", # Tiebreaker: id ), reverse=reverse_order, ) return docs def _apply_field_sort( self, documents: Sequence[TDocument], sort: CollectionSort, ) -> list[TDocument]: docs = list(documents) for field_name, direction in reversed(sort): docs.sort( key=lambda d: cast(Any, d.get(field_name)), reverse=direction == SortDirection.DESC, ) return docs def _apply_cursor_filter( self, documents: list[TDocument], cursor: Cursor, sort_direction: SortDirection, ) -> list[TDocument]: cursor_creation_utc = str(cursor.creation_utc) cursor_id = str(cursor.id) result = [] for doc in documents: doc_creation_utc = str(doc.get("creation_utc", "")) doc_id = str(doc.get("id", "")) if sort_direction == SortDirection.DESC: # For descending order pagination, include documents that come after the cursor # This matches the MongoDB query pattern: # { "$or": [ # { "creation_utc": { "$lt": cursor_creation_utc } }, # { "creation_utc": cursor_creation_utc, "id": { "$lt": cursor_id } } # ]} if doc_creation_utc < cursor_creation_utc or ( doc_creation_utc == cursor_creation_utc and doc_id < cursor_id ): result.append(doc) else: # SortDirection.ASC # For ascending order pagination, include documents that come after the cursor # { "$or": [ # { "creation_utc": { "$gt": cursor_creation_utc } }, # { "creation_utc": cursor_creation_utc, "id": { "$gt": cursor_id } } # ]} if doc_creation_utc > cursor_creation_utc or ( doc_creation_utc == cursor_creation_utc and doc_id > cursor_id ): result.append(doc) return result @override async def find_one( self, filters: Where, sort: Optional[CollectionSort] = None, ) -> Optional[TDocument]: matching_documents = [doc for doc in self._documents if matches_filters(filters, doc)] if sort: matching_documents = self._apply_field_sort(matching_documents, sort) for doc in matching_documents: return doc return None @override async def ensure_indexes( self, indexes: Sequence[CollectionIndex], ) -> None: return None @override async def insert_one( self, document: TDocument, ) -> InsertResult: ensure_is_total(document, self._schema) self._documents.append(document) return InsertResult(acknowledged=True) @override async def update_one( self, filters: Where, params: TDocument, upsert: bool = False, ) -> UpdateResult[TDocument]: for i, d in enumerate(self._documents): if matches_filters(filters, d): self._documents[i] = cast(TDocument, {**self._documents[i], **params}) return UpdateResult( acknowledged=True, matched_count=1, modified_count=1, updated_document=self._documents[i], ) if upsert: await self.insert_one(params) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=params, ) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=None, ) @override async def delete_one( self, filters: Where, ) -> DeleteResult[TDocument]: for i, d in enumerate(self._documents): if matches_filters(filters, d): document = self._documents.pop(i) return DeleteResult(deleted_count=1, acknowledged=True, deleted_document=document) return DeleteResult( acknowledged=True, deleted_count=0, deleted_document=None, ) ================================================ FILE: src/parlant/adapters/loggers/opentelemetry.py ================================================ import os from typing import Any, MutableMapping import structlog from types import TracebackType from typing_extensions import Self, override from opentelemetry.sdk.resources import Resource from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler from opentelemetry.sdk._logs.export import BatchLogRecordProcessor from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( OTLPLogExporter as GrpcOTLPLogExporter, ) from opentelemetry.exporter.otlp.proto.http._log_exporter import ( OTLPLogExporter as HttpOTLPLogExporter, ) from parlant.core.loggers import LogLevel, TracingLogger from parlant.core.tracer import Tracer class OpenTelemetryLogger(TracingLogger): """TracingLogger with OpenTelemetry log export via OTLP (gRPC or HTTP).""" def __init__( self, tracer: Tracer, log_level: LogLevel = LogLevel.DEBUG, logger_id: str | None = None, ) -> None: super().__init__(tracer=tracer, log_level=log_level, logger_id=logger_id) self._service_name = os.getenv("OTEL_SERVICE_NAME", "parlant") self._logger_provider: LoggerProvider self._log_exporter: GrpcOTLPLogExporter | HttpOTLPLogExporter self._log_processor: BatchLogRecordProcessor self._logging_handler: LoggingHandler async def __aenter__(self) -> Self: resource = Resource.create({"service.name": self._service_name}) endpoint = os.environ["OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"] insecure = os.getenv("OTEL_EXPORTER_OTLP_INSECURE", "false").lower() == "true" protocol = os.getenv("OTEL_EXPORTER_OTLP_PROTOCOL", "grpc").lower() match protocol: case "http/protobuf": self._log_exporter = HttpOTLPLogExporter(endpoint=endpoint) case "http/json": raise ValueError( "http/json protocol is not supported for logs exporter. please use http/protobuf or grpc." ) case "grpc": self._log_exporter = GrpcOTLPLogExporter( endpoint=endpoint, insecure=insecure, ) case _: raise ValueError(f"Unsupported OTLP protocol: {protocol}") self._logger_provider = LoggerProvider(resource=resource) self._log_processor = BatchLogRecordProcessor( exporter=self._log_exporter, schedule_delay_millis=2000, ) self._logger_provider.add_log_record_processor(self._log_processor) self._logging_handler = LoggingHandler( level=self.log_level.to_logging_level(), logger_provider=self._logger_provider, ) self.raw_logger.addHandler(self._logging_handler) self._inject_structlog_processors() return self async def __aexit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, ) -> bool: self._logger_provider.shutdown() # type: ignore self.raw_logger.removeHandler(self._logging_handler) return False @override def set_level(self, log_level: LogLevel) -> None: super().set_level(log_level) if self._logging_handler is not None: self._logging_handler.setLevel(log_level.to_logging_level()) def _inject_structlog_processors(self) -> None: """Add trace_id/scopes as structured fields (OTEL attributes).""" def _add_attributes( _: Any, # logger method: str, event_dict: MutableMapping[str, Any], ) -> MutableMapping[str, Any]: level = event_dict.get("actual_level", event_dict.get("level", method)) event_dict.pop("actual_level", None) event_dict.pop("level", None) event_dict["severity_text"] = str(level).upper() event_dict["trace_id"] = self._tracer.trace_id event_dict["span_id"] = self._tracer.span_id if scope := self.current_scope: event_dict["scope"] = scope return event_dict self._logger = structlog.wrap_logger( self.raw_logger, processors=[ structlog.stdlib.add_log_level, _add_attributes, structlog.stdlib.PositionalArgumentsFormatter(), structlog.processors.StackInfoRenderer(), structlog.processors.format_exc_info, structlog.stdlib.render_to_log_kwargs, ], wrapper_class=structlog.make_filtering_bound_logger( 0 ), # Avoids doing the level check twice. ) @override def trace(self, message: str) -> None: if self.log_level != LogLevel.TRACE: return self._logger.debug(message, actual_level="trace") @override def debug(self, message: str) -> None: self._logger.debug(message) @override def info(self, message: str) -> None: self._logger.info(message) @override def warning(self, message: str) -> None: self._logger.warning(message) @override def error(self, message: str) -> None: self._logger.error(message) @override def critical(self, message: str) -> None: self._logger.critical(message) ================================================ FILE: src/parlant/adapters/loggers/websocket.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import asyncio from collections import deque from dataclasses import dataclass from typing import Any from fastapi import WebSocket from typing_extensions import override from parlant.core.engines.alpha.entity_context import EntityContext from parlant.core.common import UniqueId, generate_id from parlant.core.tracer import Tracer from parlant.core.loggers import TracingLogger, LogLevel @dataclass(frozen=True) class WebSocketSubscription: socket: WebSocket expiration: asyncio.Event class WebSocketLogger(TracingLogger): def __init__( self, tracer: Tracer, log_level: LogLevel = LogLevel.DEBUG, logger_id: str | None = None, ) -> None: super().__init__(tracer, log_level, logger_id) self._message_queue = deque[Any]() self._messages_in_queue = asyncio.Semaphore(0) self._socket_subscriptions: dict[UniqueId, WebSocketSubscription] = {} self._lock = asyncio.Lock() def _enqueue_message(self, timestamp: str, level: str, message: str) -> None: payload = { "level": level, "trace_id": self._tracer.trace_id, "message": message, } if context_creation := EntityContext.get_context_creation(): payload["message"] = f"[T+{round(context_creation.elapsed, 3)}s]{message}" self._message_queue.append(payload) self._messages_in_queue.release() async def subscribe(self, web_socket: WebSocket) -> WebSocketSubscription: socket_id = generate_id() subscription = WebSocketSubscription(web_socket, asyncio.Event()) async with self._lock: self._socket_subscriptions[socket_id] = subscription return subscription def _timestamp(self) -> str: return round(asyncio.get_event_loop().time(), 3).__str__() @override def trace(self, message: str) -> None: self._enqueue_message(self._timestamp(), "TRACE", f"{self.current_scope} {message}") @override def debug(self, message: str) -> None: self._enqueue_message(self._timestamp(), "DEBUG", f"{self.current_scope} {message}") @override def info(self, message: str) -> None: self._enqueue_message(self._timestamp(), "INFO", f"{self.current_scope} {message}") @override def warning(self, message: str) -> None: self._enqueue_message(self._timestamp(), "WARNING", f"{self.current_scope} {message}") @override def error(self, message: str) -> None: self._enqueue_message(self._timestamp(), "ERROR", f"{self.current_scope} {message}") @override def critical(self, message: str) -> None: self._enqueue_message(self._timestamp(), "CRITICAL", f"{self.current_scope} {message}") async def start(self) -> None: try: while True: try: await self._messages_in_queue.acquire() payload = self._message_queue.popleft() async with self._lock: socket_subscriptions = dict(self._socket_subscriptions) expired_ids = set() for socket_id, subscription in socket_subscriptions.items(): try: await subscription.socket.send_json(payload) except Exception: expired_ids.add(socket_id) async with self._lock: for socket_id in expired_ids: subscription = self._socket_subscriptions.pop(socket_id) subscription.expiration.set() except asyncio.CancelledError: return finally: async with self._lock: for socket_id, subscription in self._socket_subscriptions.items(): subscription.expiration.set() ================================================ FILE: src/parlant/adapters/meter/opentelemetry.py ================================================ from __future__ import annotations import asyncio import os from types import TracebackType from typing import AsyncGenerator, Mapping from typing_extensions import override, Self from contextlib import asynccontextmanager from opentelemetry import metrics from opentelemetry.metrics import Counter as OTelCounter, Histogram as OTelHistogram from opentelemetry.sdk.metrics import ( MeterProvider, ) from opentelemetry.sdk.metrics.export import ( PeriodicExportingMetricReader, ) from opentelemetry.sdk.resources import Resource from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import ( OTLPMetricExporter as GrpcOTLPMetricExporter, ) from opentelemetry.exporter.otlp.proto.http.metric_exporter import ( OTLPMetricExporter as HttpOTLPMetricExporter, ) from parlant.core.meter import Counter, DurationHistogram, Meter class OpenTelemetryCounter(Counter): def __init__(self, otel_counter: OTelCounter) -> None: self._otel_counter = otel_counter @override async def increment( self, value: int, attributes: Mapping[str, str] | None = None, ) -> None: self._otel_counter.add(value, attributes) class OpenTelemetryHistogram(DurationHistogram): def __init__(self, otel_histogram: OTelHistogram) -> None: self._otel_histogram = otel_histogram @override async def record( self, value: float, attributes: Mapping[str, str] | None = None, ) -> None: self._otel_histogram.record(value, attributes) @override @asynccontextmanager async def measure( self, attributes: Mapping[str, str] | None = None, ) -> AsyncGenerator[None, None]: start_time = asyncio.get_running_loop().time() try: yield finally: duration = ( asyncio.get_running_loop().time() - start_time ) * 1000 # Convert to milliseconds await self.record(duration, attributes) class OpenTelemetryMeter(Meter): def __init__(self) -> None: self._service_name = os.getenv("OTEL_SERVICE_NAME", "parlant") self._meter: metrics.Meter self._metric_exporter: GrpcOTLPMetricExporter | HttpOTLPMetricExporter self._meter_provider: MeterProvider async def __aenter__(self) -> Self: resource = Resource.create({"service.name": self._service_name}) endpoint = os.environ["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"] insecure = os.getenv("OTEL_EXPORTER_OTLP_INSECURE", "false").lower() == "true" protocol = os.getenv("OTEL_EXPORTER_OTLP_PROTOCOL", "grpc").lower() match protocol: case "http/protobuf": self._metric_exporter = HttpOTLPMetricExporter(endpoint=endpoint) case "http/json": raise ValueError( "http/json protocol is not supported for metrics exporter. please use http/protobuf or grpc." ) case "grpc": self._metric_exporter = GrpcOTLPMetricExporter( endpoint=endpoint, insecure=insecure, ) case _: raise ValueError(f"Unsupported OTLP protocol: {protocol}") metric_reader = PeriodicExportingMetricReader( exporter=self._metric_exporter, export_interval_millis=int(os.getenv("OTEL_METRIC_EXPORT_INTERVAL", "3000")), ) self._meter_provider = MeterProvider( resource=resource, metric_readers=[metric_reader], ) metrics.set_meter_provider(self._meter_provider) self._meter = metrics.get_meter(__name__) return self async def __aexit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, ) -> bool: self._meter_provider.force_flush() self._meter_provider.shutdown() return False @override def create_counter( self, name: str, description: str, ) -> Counter: otel_counter = self._meter.create_counter( name=name, description=description, ) return OpenTelemetryCounter(otel_counter) @override def create_custom_histogram( self, name: str, description: str, unit: str, ) -> OpenTelemetryHistogram: otel_histogram = self._meter.create_histogram( name=name, description=description, unit=unit, ) return OpenTelemetryHistogram(otel_histogram) @override def create_duration_histogram( self, name: str, description: str, ) -> OpenTelemetryHistogram: return self.create_custom_histogram(name, description, "ms") ================================================ FILE: src/parlant/adapters/nlp/anthropic_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time from pydantic import ValidationError from anthropic import ( APIConnectionError, APIResponseValidationError, APITimeoutError, AsyncAnthropic, InternalServerError, RateLimitError, ) # type: ignore from typing import Any, Mapping from typing_extensions import override import jsonfinder # type: ignore import os from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import JinaAIEmbedder from parlant.core.engines.alpha.canned_response_generator import CannedResponseSelectionSchema from parlant.core.engines.alpha.guideline_matching.generic.disambiguation_batch import ( DisambiguationGuidelineMatchesSchema, ) from parlant.core.engines.alpha.guideline_matching.generic.journey.journey_backtrack_node_selection import ( JourneyBacktrackNodeSelectionSchema, ) from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.embedding import Embedder from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.generation import StreamingTextGenerator from parlant.core.nlp.tokenization import EstimatingTokenizer class AnthropicEstimatingTokenizer(EstimatingTokenizer): def __init__(self, client: AsyncAnthropic, model_name: str) -> None: self._client = client self.model_name = model_name @override async def estimate_token_count(self, prompt: str) -> int: result = await self._client.messages.count_tokens( model=self.model_name, messages=[{"role": "assistant", "content": prompt}], ) return result.input_tokens # type: ignore[no-any-return] class AnthropicAISchematicGenerator(BaseSchematicGenerator[T]): supported_hints = ["temperature"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncAnthropic(api_key=os.environ.get("ANTHROPIC_API_KEY")) self._estimating_tokenizer = AnthropicEstimatingTokenizer(self._client, model_name) @property @override def id(self) -> str: return f"anthropic/{self.model_name}" @property @override def tokenizer(self) -> AnthropicEstimatingTokenizer: return self._estimating_tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, RateLimitError, APIResponseValidationError, ) ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Anthropic LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() anthropic_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} t_start = time.time() try: response = await self._client.messages.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, max_tokens=4096, **anthropic_api_arguments, ) except RateLimitError: self.logger.error( ( "Anthropic API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Anthropic account balance and billing status.\n" "- Review your API usage limits in Anthropic's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://docs.anthropic.com/claude/reference/rate-limits \n" ), ) raise t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.content[0].text try: json_content = normalize_json_output(raw_content) json_object = jsonfinder.only_json(json_content)[2] except Exception: self.logger.error( f"Failed to extract JSON returned by {self.model_name}:\n{raw_content}" ) raise try: model_content = self.schema.model_validate(json_object) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.input_tokens, output_tokens=response.usage.output_tokens, ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.input_tokens, output_tokens=response.usage.output_tokens, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class Claude_Sonnet_3_5(AnthropicAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="claude-3-5-sonnet-20241022", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 200 * 1024 class Claude_Sonnet_4(AnthropicAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="claude-sonnet-4-20250514", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 200 * 1024 class Claude_Opus_4_1(AnthropicAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="claude-opus-4-1-20250805", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 200 * 1024 class AnthropicService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("ANTHROPIC_API_KEY"): return """\ You're using the Anthropic NLP service, but ANTHROPIC_API_KEY is not set. Please set ANTHROPIC_API_KEY in your environment before running Parlant. """ return None def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: self.logger = logger self._tracer = tracer self._meter = meter self.logger.info("Initialized AnthropicService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> AnthropicAISchematicGenerator[T]: if ( t == JourneyBacktrackNodeSelectionSchema or t == DisambiguationGuidelineMatchesSchema or t == CannedResponseSelectionSchema ): return Claude_Opus_4_1[t](self.logger, self._tracer, self._meter) # type: ignore return Claude_Sonnet_4[t](self.logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return JinaAIEmbedder(self.logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/aws_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time from anthropic import ( AsyncAnthropicBedrock, APIConnectionError, APIResponseValidationError, APITimeoutError, InternalServerError, RateLimitError, ) # type: ignore from pydantic import ValidationError from typing import Any, Mapping from typing_extensions import override import jsonfinder # type: ignore import os import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import JinaAIEmbedder from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.embedding import Embedder from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.tokenization import EstimatingTokenizer class AnthropicBedrockEstimatingTokenizer(EstimatingTokenizer): def __init__(self) -> None: self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return int(len(tokens) * 1.15) class AnthropicBedrockAISchematicGenerator(BaseSchematicGenerator[T]): supported_hints = ["temperature"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncAnthropicBedrock( aws_access_key=os.environ["AWS_ACCESS_KEY_ID"], aws_secret_key=os.environ["AWS_SECRET_ACCESS_KEY"], aws_region=os.environ["AWS_REGION"], aws_session_token=os.environ.get("AWS_SESSION_TOKEN", None), ) self._estimating_tokenizer = AnthropicBedrockEstimatingTokenizer() @property @override def id(self) -> str: return f"bedrock/{self.model_name}" @property @override def tokenizer(self) -> AnthropicBedrockEstimatingTokenizer: return self._estimating_tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, RateLimitError, APIResponseValidationError, ) ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"AWS LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() anthropic_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} t_start = time.time() try: response = await self._client.messages.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, max_tokens=4096, **anthropic_api_arguments, ) except RateLimitError: self.logger.error( "AWS Bedrock API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your AWS Bedrock account balance and billing status.\n" "- Review your API usage limits in AWS Bedrock's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://us-east-1.console.aws.amazon.com/servicequotas/home/services/bedrock/quotas", ) raise t_end = time.time() raw_content = response.content[0].text try: json_content = normalize_json_output(raw_content) json_object = jsonfinder.only_json(json_content)[2] except Exception: self.logger.error( f"Failed to extract JSON returned by {self.model_name}:\n{raw_content}" ) raise try: model_content = self.schema.model_validate(json_object) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.input_tokens, output_tokens=response.usage.output_tokens, ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.input_tokens, output_tokens=response.usage.output_tokens, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class Claude_Sonnet_3_5(AnthropicBedrockAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="anthropic.claude-3-5-sonnet-20240620-v1:0", logger=logger, tracer=tracer, meter=meter, ) @override @property def max_tokens(self) -> int: return 200 * 1024 class BedrockService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("ANTHROPIC_API_KEY"): return """\ You're using the AWS Bedrock NLP service, but some environment variables are missing. Please consider setting the following your environment before running Parlant. - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_REGION - AWS_SESSION_TOKEN """ return None def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: self._logger = logger self._tracer = tracer self._meter = meter @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> AnthropicBedrockAISchematicGenerator[T]: return Claude_Sonnet_3_5[t](self._logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return JinaAIEmbedder(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/azure_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import time from openai import ( AsyncAzureOpenAI, APIConnectionError, APIResponseValidationError, APITimeoutError, InternalServerError, RateLimitError, ) # type: ignore from azure.identity.aio import DefaultAzureCredential # type: ignore from typing import Any, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ModerationService, NoModeration class AzureEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self.encoding = tiktoken.encoding_for_model(model_name) async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class AzureSchematicGenerator(BaseSchematicGenerator[T]): supported_azure_params = ["temperature", "logit_bias", "max_tokens"] supported_hints = supported_azure_params + ["strict"] unsupported_params_by_model: dict[str, list[str]] = { "gpt-5": ["temperature"], } def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, client: AsyncAzureOpenAI, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = client self._tokenizer = AzureEstimatingTokenizer(model_name=self.model_name) @property def id(self) -> str: return f"azure/{self.model_name}" @property def tokenizer(self) -> AzureEstimatingTokenizer: return self._tokenizer def _list_arguments(self, hints: Mapping[str, Any]) -> Mapping[str, Any]: exclude_params = [ k for k in self.supported_azure_params for prefix, excluded in self.unsupported_params_by_model.items() if self.model_name.startswith(prefix) and k in excluded ] return { k: v for k, v in hints.items() if k in self.supported_azure_params and k not in exclude_params } @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, RateLimitError, APIResponseValidationError, ) ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Azure LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() azure_api_arguments = self._list_arguments(hints) if hints.get("strict", False): t_start = time.time() try: response = await self._client.beta.chat.completions.parse( messages=[{"role": "user", "content": prompt}], model=self.model_name, response_format=self.schema, **azure_api_arguments, ) except RateLimitError: self.logger.error( "Azure API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Azure account balance and billing status.\n" "- Review your API usage limits in Azure's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://learn.microsoft.com/en-us/azure/ai-services/openai/quotas-limits\n", ) raise t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) parsed_object = response.choices[0].message.parsed assert parsed_object assert response.usage await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, cached_input_tokens=response.usage.prompt_tokens_details.cached_tokens or 0 if response.usage.prompt_tokens_details else 0, ) return SchematicGenerationResult[T]( content=parsed_object, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra=( { "cached_input_tokens": response.usage.prompt_tokens_details.cached_tokens or 0 } if response.usage.prompt_tokens_details else {} ), ), ), ) else: t_start = time.time() try: response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, response_format={"type": "json_object"}, **azure_api_arguments, ) except RateLimitError: self.logger.error( "Azure API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Azure account balance and billing status.\n" "- Review your API usage limits in Azure's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://learn.microsoft.com/en-us/azure/ai-services/openai/quotas-limits\n", ) raise t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.choices[0].message.content or "{}" try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) assert response.usage return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra=( { "cached_input_tokens": response.usage.prompt_tokens_details.cached_tokens or 0 } if response.usage.prompt_tokens_details else {} ), ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise def create_azure_client() -> AsyncAzureOpenAI: """Create an Azure OpenAI client with appropriate authentication.""" azure_endpoint = os.environ["AZURE_ENDPOINT"] # Check if API key is provided (backward compatibility) if os.environ.get("AZURE_API_KEY"): return AsyncAzureOpenAI( api_key=os.environ["AZURE_API_KEY"], azure_endpoint=azure_endpoint, api_version=os.environ.get("AZURE_API_VERSION", "2024-08-01-preview"), ) else: # Use Azure AD authentication try: credential = DefaultAzureCredential() async def token_provider() -> str: """Token provider that requests tokens with the correct scope for Azure OpenAI.""" try: token = await credential.get_token( "https://cognitiveservices.azure.com/.default" ) return str(token.token) except Exception as e: raise RuntimeError( f"Failed to get Azure AD token: {e}\n\n" "Please ensure you are authenticated with Azure AD using one of:\n" "1. Azure CLI: `az login`\n" "2. Service Principal environment variables:\n" " - AZURE_CLIENT_ID\n" " - AZURE_CLIENT_SECRET\n" " - AZURE_TENANT_ID\n" "3. Managed Identity (if running on Azure)\n\n" "For more details, see: https://docs.microsoft.com/en-us/python/api/overview/azure/identity-readme" ) from e return AsyncAzureOpenAI( azure_ad_token_provider=token_provider, azure_endpoint=azure_endpoint, api_version=os.environ.get("AZURE_API_VERSION", "2024-08-01-preview"), ) except Exception as e: raise RuntimeError( f"Failed to initialize Azure AD authentication: {e}\n\n" "Please ensure you are authenticated with Azure AD using one of:\n" "1. Azure CLI: `az login`\n" "2. Service Principal environment variables:\n" " - AZURE_CLIENT_ID\n" " - AZURE_CLIENT_SECRET\n" " - AZURE_TENANT_ID\n" "3. Managed Identity (if running on Azure)\n\n" "For more details, see: https://docs.microsoft.com/en-us/python/api/overview/azure/identity-readme" ) from e class CustomAzureSchematicGenerator(AzureSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: _client = create_azure_client() super().__init__( model_name=os.environ["AZURE_GENERATIVE_MODEL_NAME"], logger=logger, tracer=tracer, meter=meter, client=_client, ) @property def max_tokens(self) -> int: return int(os.environ.get("AZURE_GENERATIVE_MODEL_WINDOW", 4096)) class GPT_4o(AzureSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: _client = create_azure_client() super().__init__( model_name="gpt-4o", logger=logger, tracer=tracer, meter=meter, client=_client ) @property def max_tokens(self) -> int: return 128 * 1024 class GPT_4o_Mini(AzureSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: _client = create_azure_client() super().__init__( model_name="gpt-4o-mini", logger=logger, tracer=tracer, meter=meter, client=_client ) self._token_estimator = AzureEstimatingTokenizer(model_name=self.model_name) @property def max_tokens(self) -> int: return 128 * 1024 class AzureEmbedder(BaseEmbedder): supported_arguments = ["dimensions"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, client: AsyncAzureOpenAI, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = client self._tokenizer = AzureEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"azure/{self.model_name}" @property @override def tokenizer(self) -> AzureEstimatingTokenizer: return self._tokenizer @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: filtered_hints = {k: v for k, v in hints.items() if k in self.supported_arguments} try: response = await self._client.embeddings.create( model=self.model_name, input=texts, **filtered_hints, ) except RateLimitError: self.logger.error( "Azure API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Azure account balance and billing status.\n" "- Review your API usage limits in Azure's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://learn.microsoft.com/en-us/azure/ai-services/openai/quotas-limits\n", ) raise vectors = [data_point.embedding for data_point in response.data] return EmbeddingResult(vectors=vectors) class CustomAzureEmbedder(AzureEmbedder): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: _client = create_azure_client() super().__init__( model_name=os.environ["AZURE_EMBEDDING_MODEL_NAME"], logger=logger, tracer=tracer, meter=meter, client=_client, ) @property @override def max_tokens(self) -> int: return int(os.environ["AZURE_EMBEDDING_MODEL_WINDOW"]) @property def dimensions(self) -> int: return int(os.environ["AZURE_EMBEDDING_MODEL_DIMS"]) class AzureTextEmbedding3Large(AzureEmbedder): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: _client = create_azure_client() super().__init__( model_name="text-embedding-3-large", logger=logger, tracer=tracer, meter=meter, client=_client, ) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 3072 class AzureTextEmbedding3Small(AzureEmbedder): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: _client = create_azure_client() super().__init__( model_name="text-embedding-3-small", logger=logger, tracer=tracer, meter=meter, client=_client, ) @property def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 1536 class AzureService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("AZURE_ENDPOINT"): return """\ You're using the Azure NLP service, but AZURE_ENDPOINT is not set. Please set AZURE_ENDPOINT in your environment before running Parlant. Required environment variables: - AZURE_ENDPOINT Authentication options (choose one): 1. Azure AD (recommended): - Ensure you're authenticated via Azure CLI: `az login` - Or set up managed identity/service principal authentication 2. API Key (legacy): - AZURE_API_KEY You can also set any specific models you'd like to use, using a few more variables: - AZURE_GENERATIVE_MODEL_NAME (e.g., gpt-4o) - AZURE_GENERATIVE_MODEL_WINDOW (size of the generative model's context window) - AZURE_EMBEDDING_MODEL_NAME (e.g., text-embedding-3-large) - AZURE_EMBEDDING_MODEL_DIMS (dimensions of the embedding model) - AZURE_EMBEDDING_MODEL_WINDOW (size of of the embedding model's context window) For Azure AD authentication, ensure your identity has the "Cognitive Services OpenAI User" role on the Azure OpenAI resource. """ # Check authentication method has_api_key = bool(os.environ.get("AZURE_API_KEY")) if has_api_key: # API key authentication is configured return None # Check Azure AD authentication try: from azure.identity import DefaultAzureCredential # type: ignore credential = DefaultAzureCredential() # Try to get a token to verify authentication works import asyncio async def test_auth() -> bool: try: token = credential.get_token("https://cognitiveservices.azure.com/.default") return token is not None except Exception: return False # Run the async test try: loop = asyncio.get_event_loop() if loop.is_running(): # If we're already in an async context, we can't test synchronously # Just check if we can create the credential return None else: auth_works = loop.run_until_complete(test_auth()) if auth_works: return None except RuntimeError: # No event loop, create a new one auth_works = asyncio.run(test_auth()) if auth_works: return None except Exception: pass # If we get here, neither authentication method is working return """\ Azure authentication is not properly configured. Please choose one of the following authentication methods: 1. API Key Authentication (Legacy): Set the AZURE_API_KEY environment variable with your Azure OpenAI API key. 2. Azure AD Authentication (Recommended): Ensure you're authenticated using one of these methods: a) Azure CLI (for development): Run: az login b) Service Principal (for production): Set these environment variables: - AZURE_CLIENT_ID - AZURE_CLIENT_SECRET - AZURE_TENANT_ID c) Managed Identity (if running on Azure): Ensure your Azure resource has managed identity enabled d) Environment Credential: Set these environment variables: - AZURE_CLIENT_ID - AZURE_CLIENT_SECRET - AZURE_TENANT_ID e) Workload Identity (for Kubernetes): Set these environment variables: - AZURE_CLIENT_ID - AZURE_TENANT_ID - AZURE_FEDERATED_TOKEN_FILE Important: For Azure AD authentication, ensure your identity has the "Cognitive Services OpenAI User" role on the Azure OpenAI resource. For more details on Azure AD authentication options, see: https://docs.microsoft.com/en-us/python/api/overview/azure/identity-readme """ def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.logger = logger self._tracer = tracer self._meter = meter @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> AzureSchematicGenerator[T]: if os.environ.get("AZURE_GENERATIVE_MODEL_NAME"): return CustomAzureSchematicGenerator[t]( # type: ignore logger=self.logger, tracer=self._tracer, meter=self._meter ) return GPT_4o[t](self.logger, self._tracer, self._meter) # type: ignore async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: if os.environ.get("AZURE_EMBEDDING_MODEL_NAME"): return CustomAzureEmbedder(self.logger, self._tracer, self._meter) return AzureTextEmbedding3Large(self.logger, self._tracer, self._meter) async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/cerebras_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time from pydantic import ValidationError from cerebras.cloud.sdk import AsyncCerebras from cerebras.cloud.sdk import ( RateLimitError, APIConnectionError, APITimeoutError, InternalServerError, ) from typing import Any, Mapping from typing_extensions import override import jsonfinder # type: ignore import os import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import JinaAIEmbedder from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.embedding import Embedder from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.tokenization import EstimatingTokenizer class LlamaEstimatingTokenizer(EstimatingTokenizer): def __init__(self) -> None: self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) + 36 class CerebrasSchematicGenerator(BaseSchematicGenerator[T]): supported_hints = ["temperature"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncCerebras(api_key=os.environ.get("CEREBRAS_API_KEY")) @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, RateLimitError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Cerebras LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() cerebras_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} t_start = time.time() try: response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, response_format={ "type": "json_schema", "json_schema": { "schema": self.schema.model_json_schema(), "name": self.schema.__name__, "strict": True, }, }, **cerebras_api_arguments, ) except RateLimitError: self.logger.error( "Cerebras API rate limit exceeded.\n" "Your account may have reached the maximum number of requests allowed per minute for the tier you are using.\n" "Please contact with Cerebras support for more information." ) raise t_end = time.time() if response.usage: # type: ignore self.logger.trace(response.usage.model_dump_json(indent=2)) # type: ignore raw_content = response.choices[0].message.content or "{}" # type: ignore try: json_content = normalize_json_output(raw_content) json_object = jsonfinder.only_json(json_content)[2] except Exception: self.logger.error( f"Failed to extract JSON returned by {self.model_name}:\n{raw_content}" ) raise try: model_content = self.schema.model_validate(json_object) await record_llm_metrics( self.meter, self.model_name, input_tokens=response.usage.prompt_tokens, # type: ignore output_tokens=response.usage.completion_tokens, # type: ignore ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, # type: ignore output_tokens=response.usage.completion_tokens, # type: ignore extra={}, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class Llama3_3_8B(CerebrasSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="llama3.1-8b", logger=logger, tracer=tracer, meter=meter, ) self._estimating_tokenizer = LlamaEstimatingTokenizer() @property @override def id(self) -> str: return self.model_name @property @override def max_tokens(self) -> int: return 8192 @property @override def tokenizer(self) -> LlamaEstimatingTokenizer: return self._estimating_tokenizer class Llama3_3_70B(CerebrasSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="llama3.3-70b", logger=logger, tracer=tracer, meter=meter, ) self._estimating_tokenizer = LlamaEstimatingTokenizer() @property @override def id(self) -> str: return self.model_name @property @override def tokenizer(self) -> LlamaEstimatingTokenizer: return self._estimating_tokenizer @property @override def max_tokens(self) -> int: return 32 * 1024 class CerebrasService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("CEREBRAS_API_KEY"): return """\ You're using the OpenAI NLP service, but CEREBRAS_API_KEY is not set. Please set CEREBRAS_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.logger = logger self._tracer = tracer self.meter = meter self.logger.info("Initialized CerebrasService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> CerebrasSchematicGenerator[T]: return Llama3_3_70B[t](self.logger, self._tracer, self.meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return JinaAIEmbedder(self.logger, self._tracer, self.meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/common.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from parlant.core.meter import Counter, Meter def normalize_json_output(raw_output: str) -> str: json_start = raw_output.find("```json") if json_start != -1: json_start = json_start + 7 else: json_start = 0 json_end = raw_output[json_start:].rfind("```") if json_end == -1: json_end = len(raw_output[json_start:]) return raw_output[json_start : json_start + json_end].strip() _INPUT_TOKENS_COUNTER: Counter _OUTPUT_TOKENS_COUNTER: Counter _CACHED_TOKENS_COUNTER: Counter _COUNTERS_INITIALIZED = False async def record_llm_metrics( meter: Meter, model_name: str, schema_name: str, input_tokens: int, output_tokens: int, cached_input_tokens: int = 0, ) -> None: global _COUNTERS_INITIALIZED global _INPUT_TOKENS_COUNTER global _OUTPUT_TOKENS_COUNTER global _CACHED_TOKENS_COUNTER if not _COUNTERS_INITIALIZED: _INPUT_TOKENS_COUNTER = meter.create_counter( name="input_tokens", description="Number of input tokens sent to a LLM model", ) _OUTPUT_TOKENS_COUNTER = meter.create_counter( name="output_tokens", description="Number of output tokens received from a LLM model", ) _CACHED_TOKENS_COUNTER = meter.create_counter( name="cached_input_tokens", description="Number of input tokens served from cache for a LLM model", ) _COUNTERS_INITIALIZED = True await _INPUT_TOKENS_COUNTER.increment( input_tokens, {"model_name": model_name, "schema_name": schema_name}, ) await _OUTPUT_TOKENS_COUNTER.increment( output_tokens, {"model_name": model_name, "schema_name": schema_name}, ) await _CACHED_TOKENS_COUNTER.increment( cached_input_tokens, {"model_name": model_name, "schema_name": schema_name}, ) ================================================ FILE: src/parlant/adapters/nlp/deepseek_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import time from openai import ( APIConnectionError, APIResponseValidationError, APITimeoutError, AsyncClient, ConflictError, InternalServerError, RateLimitError, ) from typing import Any, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import JinaAIEmbedder from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import Embedder from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( ModerationService, NoModeration, ) class DeepSeekEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class DeepSeekSchematicGenerator(BaseSchematicGenerator[T]): supported_deepseek_params = ["temperature", "logit_bias", "max_tokens"] supported_hints = supported_deepseek_params + ["strict"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient( base_url="https://api.deepseek.com", api_key=os.environ["DEEPSEEK_API_KEY"], ) self._tokenizer = DeepSeekEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"deepseek/{self.model_name}" @property @override def tokenizer(self) -> DeepSeekEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"DeepSeek LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() deepseek_api_arguments = { k: v for k, v in hints.items() if k in self.supported_deepseek_params } t_start = time.time() response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, max_tokens=8192, response_format={"type": "json_object"}, **deepseek_api_arguments, ) t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.choices[0].message.content or "{}" try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) assert response.usage await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, cached_input_tokens=getattr( response, "usage.prompt_cache_hit_tokens", 0, ), ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={ "cached_input_tokens": getattr( response, "usage.prompt_cache_hit_tokens", 0, ) }, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class DeepSeek_Chat(DeepSeekSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="deepseek-chat", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class DeepSeekService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("DEEPSEEK_API_KEY"): return """\ You're using the DeepSeek NLP service, but DEEPSEEK_API_KEY is not set. Please set DEEPSEEK_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self._logger = logger self._tracer = tracer self._meter = meter self._logger.info("Initialized DeepSeekService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> DeepSeekSchematicGenerator[T]: return DeepSeek_Chat[t](self._logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return JinaAIEmbedder(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/emcie_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations from pprint import pformat import re import time from typing import Any, AsyncIterator, Callable, Mapping, TypeAlias, cast from httpx import AsyncClient import httpx from typing_extensions import Literal, override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, ModelSize, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, BaseStreamingTextGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( ModerationService, NoModeration, ) from parlant.core.tracer import Tracer from parlant.core.version import VERSION ERROR_MESSAGE = ( "Emcie API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Emcie account balance and billing status.\n" "- Review your API usage limits in Emcie's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://docs.emcie.co\n" ) GenerationModelTier: TypeAlias = Literal["jackal", "bison"] EmbeddingModelTier: TypeAlias = Literal["jackal-embedding", "bison-embedding"] ModelRole: TypeAlias = Literal["teacher", "student", "auto"] BASE_URL = os.environ.get("EMCIE_API_URL", "https://api.emcie.co/inference") # Pattern to detect word boundaries for chunking # Matches after any whitespace character _WORD_BOUNDARY_PATTERN = re.compile(r"(?<=\s)") # Number of words to buffer before yielding a chunk _WORDS_PER_CHUNK = 3 class EmcieEstimatingTokenizer(EstimatingTokenizer): def __init__(self) -> None: self.encoding = tiktoken.encoding_for_model("gpt-4.1") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class EmcieAPIError(Exception): pass class InsufficientCreditsError(EmcieAPIError): pass class RateLimitError(EmcieAPIError): pass class UnauthorizedError(EmcieAPIError): pass class EmcieSchematicGenerator(BaseSchematicGenerator[T]): supported_emcie_params = ["temperature"] def __init__( self, model_name: str, model_role: ModelRole, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._model_role = model_role self._tokenizer = EmcieEstimatingTokenizer() @property @override def id(self) -> str: return f"emcie/{self.model_name}" @property @override def tokenizer(self) -> EmcieEstimatingTokenizer: return self._tokenizer @policy( [ retry(exceptions=(RateLimitError)), retry(EmcieAPIError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Emcie LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): props = prompt.props prompt = prompt.build() else: props = {} try: t_start = time.time() timeout = httpx.Timeout( connect=30.0, read=120.0, write=30.0, pool=5.0, ) async with AsyncClient(timeout=timeout) as client: response = await client.post( f"{BASE_URL}/v1/completions", headers={ "Authorization": f"Bearer {os.environ['EMCIE_API_KEY']}", "X-Parlant-Version": VERSION, }, json={ "model_tier": self.model_name, "model_role": self._model_role, "prompt": prompt, "schema_name": self.schema.__name__, "hints": { k: v for k, v in hints.items() if k in self.supported_emcie_params }, "payload": props, }, ) if response.status_code == 429: raise RateLimitError( f"Emcie API rate limit exceeded: {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) elif response.status_code == 402: raise InsufficientCreditsError( f"Insufficient API credits for Emcie API: {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) elif response.status_code == 403: raise UnauthorizedError( f"Unauthorized access to Emcie API: {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) elif response.status_code >= 500: raise EmcieAPIError( f"Emcie API error: {response.status_code} {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) response.raise_for_status() t_end = time.time() except (InsufficientCreditsError, RateLimitError): self.logger.error(ERROR_MESSAGE) raise except EmcieAPIError as e: self.logger.error(f"Emcie API error occurred: {e}") raise except Exception as e: self.logger.error(f"Unexpected error during Emcie API call: {e}") raise response_data = response.json() usage = response_data["usage"] cost = response_data["cost"] self.logger.trace(f"Emcie usage data:\n{pformat({**usage, **cost})}") raw_content = response_data["completion"] try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=int(usage["input_tokens"]), output_tokens=int(usage["output_tokens"]), cached_input_tokens=0, ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=int(usage["input_tokens"]), output_tokens=int(usage["output_tokens"]), extra={}, ), ), ) except ValidationError as e: self.logger.error( f"Error: {e.json(indent=2)}\nJSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class Jackal(EmcieSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, model_role: ModelRole, ) -> None: super().__init__( model_name="jackal", logger=logger, tracer=tracer, meter=meter, model_role=model_role, ) @property @override def max_tokens(self) -> int: return 128 * 1024 class Bison(EmcieSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, model_role: ModelRole, ) -> None: super().__init__( model_name="bison", logger=logger, tracer=tracer, meter=meter, model_role=model_role, ) @property @override def max_tokens(self) -> int: return 128 * 1024 # ============================================================================ # Streaming Text Generators # ============================================================================ class EmcieStreamingTextGenerator(BaseStreamingTextGenerator): """Streaming text generator using Emcie's streaming API. Buffers tokens into word-sized chunks for smoother frontend rendering. """ supported_emcie_params = ["temperature"] def __init__( self, model_name: str, model_role: ModelRole, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._model_role = model_role self._tokenizer = EmcieEstimatingTokenizer() @property @override def id(self) -> str: return f"emcie-streaming/{self.model_name}" @property @override def tokenizer(self) -> EmcieEstimatingTokenizer: return self._tokenizer @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> tuple[AsyncIterator[str | None], Callable[[], UsageInfo]]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() # Track usage from the done event usage_info: UsageInfo | None = None async def chunk_generator() -> AsyncIterator[str | None]: nonlocal usage_info timeout = httpx.Timeout( connect=30.0, read=120.0, write=30.0, pool=5.0, ) # Buffer for accumulating tokens into word-sized chunks buffer = "" async with AsyncClient(timeout=timeout) as client: async with client.stream( "POST", f"{BASE_URL}/v1/completions", headers={ "Authorization": f"Bearer {os.environ['EMCIE_API_KEY']}", "X-Parlant-Version": VERSION, }, json={ "model_tier": self.model_name, "model_role": self._model_role, "prompt": prompt, "stream": True, "hints": { k: v for k, v in hints.items() if k in self.supported_emcie_params }, }, ) as response: # Check status before iterating to catch auth/rate-limit errors early if response.status_code == 429: await response.aread() response_data = response.json() self.logger.error(ERROR_MESSAGE) raise RateLimitError( f"Emcie API rate limit exceeded: {response_data['detail']['error']['message']} (RID={response_data['detail']['request_id']})" ) elif response.status_code == 402: await response.aread() response_data = response.json() self.logger.error(ERROR_MESSAGE) raise InsufficientCreditsError( f"Insufficient API credits for Emcie API: {response_data['detail']['error']['message']} (RID={response_data['detail']['request_id']})" ) elif response.status_code == 403: await response.aread() response_data = response.json() raise UnauthorizedError( f"Unauthorized access to Emcie API: {response_data['detail']['error']['message']} (RID={response_data['detail']['request_id']})" ) elif response.status_code >= 500: await response.aread() response_data = response.json() raise EmcieAPIError( f"Emcie API error: {response.status_code} {response_data['detail']['error']['message']} (RID={response_data['detail']['request_id']})" ) response.raise_for_status() # Parse SSE events event_type: str | None = None async for line in response.aiter_lines(): if line.startswith("event: "): event_type = line[7:] elif line.startswith("data: ") and event_type: data = json.loads(line[6:]) if event_type == "chunk": text = data.get("text", "") if text: buffer += text # Count word boundaries in buffer boundaries = list(_WORD_BOUNDARY_PATTERN.finditer(buffer)) if len(boundaries) >= _WORDS_PER_CHUNK: # Yield up to the last complete word boundary last_boundary = boundaries[_WORDS_PER_CHUNK - 1] chunk_text = buffer[: last_boundary.end()] buffer = buffer[last_boundary.end() :] yield chunk_text elif event_type == "done": usage = data.get("usage", {}) usage_info = UsageInfo( input_tokens=int(usage.get("input_tokens", 0)), output_tokens=int(usage.get("output_tokens", 0)), extra={}, ) self.logger.trace(f"Emcie streaming usage data:\n{pformat(data)}") # Yield any remaining content in the buffer if buffer: yield buffer buffer = "" elif event_type == "error": error_msg = data.get("error", {}).get("message", "Unknown error") raise EmcieAPIError(f"Emcie streaming error: {error_msg}") # Record metrics if we have usage info if usage_info is not None: await record_llm_metrics( self.meter, self.model_name, schema_name="streaming", input_tokens=usage_info.input_tokens, output_tokens=usage_info.output_tokens, cached_input_tokens=0, ) # Signal completion yield None def get_usage() -> UsageInfo: if usage_info is None: return UsageInfo(input_tokens=0, output_tokens=0, extra={}) return usage_info return chunk_generator(), get_usage class JackalStreaming(EmcieStreamingTextGenerator): def __init__( self, model_role: ModelRole, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__( model_name="jackal", model_role=model_role, logger=logger, tracer=tracer, meter=meter, ) class BisonStreaming(EmcieStreamingTextGenerator): def __init__( self, model_role: ModelRole, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__( model_name="bison", model_role=model_role, logger=logger, tracer=tracer, meter=meter, ) # ============================================================================ # Embedders # ============================================================================ class EmcieEmbedder(BaseEmbedder): supported_arguments = ["dimensions"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger, tracer, meter, model_name) self._tokenizer = EmcieEstimatingTokenizer() @property @override def id(self) -> str: return f"emcie/{self.model_name}" @property @override def tokenizer(self) -> EmcieEstimatingTokenizer: return self._tokenizer @policy( [ retry(exceptions=(RateLimitError)), retry(EmcieAPIError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: try: timeout = httpx.Timeout( connect=5.0, read=120.0, write=30.0, pool=5.0, ) async with AsyncClient(timeout=timeout) as client: response = await client.post( f"{BASE_URL}/v1/embeddings", headers={ "Authorization": f"Bearer {os.environ['EMCIE_API_KEY']}", "X-Parlant-Version": VERSION, }, json={ "model_tier": self.model_name, "inputs": texts, "hints": {k: v for k, v in hints.items() if k in self.supported_arguments}, }, ) if response.status_code == 429: raise RateLimitError( f"Emcie API rate limit exceeded: {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) elif response.status_code == 402: raise InsufficientCreditsError( f"Insufficient API credits for Emcie API: {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) elif response.status_code == 403: raise UnauthorizedError( f"Unauthorized access to Emcie API: {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) elif response.status_code >= 500: raise EmcieAPIError( f"Emcie API error: {response.status_code} {response.json()['detail']['error']['message']} (RID={response.json()['detail']['request_id']})" ) response.raise_for_status() except RateLimitError: self.logger.error(ERROR_MESSAGE) raise except Exception as e: self.logger.error(f"Unexpected error during Emcie API call: {e}") raise response_data = response.json() vectors = [data_point["embedding"] for data_point in response_data["data"]] return EmbeddingResult(vectors=vectors) class BisonEmbedding(EmcieEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="bison-embedding", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 3072 class JackalEmbedding(EmcieEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="jackal-embedding", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 1536 class EmcieService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("EMCIE_API_KEY"): return """\ You're using Emcie's optimized NLP service, but EMCIE_API_KEY is not set. Please set EMCIE_API_KEY in your environment before running Parlant. For alternative providers, see https://parlant.io/docs/quickstart/installation. Get an API key for Emcie by signing up at https://www.emcie.co.""" return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, model_tier: GenerationModelTier | None = None, model_role: ModelRole | None = None, ) -> None: self._logger = logger self._meter = meter self._tracer = tracer self._model_tier = model_tier or os.environ.get("EMCIE_MODEL_TIER", "jackal") self._model_role = model_role or os.environ.get("EMCIE_MODEL_ROLE", "auto") assert self._model_tier in ("jackal", "bison"), "Invalid EMCIE_MODEL_TIER" assert self._model_role in ("teacher", "student", "auto"), "Invalid EMCIE_MODEL_ROLE" self._logger.info("Initialized EmcieService") @property @override def supports_streaming(self) -> bool: return True @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: match self._model_tier: case "bison": return BisonStreaming( model_role=cast(ModelRole, self._model_role), logger=self._logger, tracer=self._tracer, meter=self._meter, ) case _: return JackalStreaming( model_role=cast(ModelRole, self._model_role), logger=self._logger, tracer=self._tracer, meter=self._meter, ) @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> EmcieSchematicGenerator[T]: match self._model_tier: case "jackal": return Jackal[t]( # type: ignore model_role=cast(ModelRole, self._model_role), logger=self._logger, tracer=self._tracer, meter=self._meter, ) case "bison": return Bison[t]( # type: ignore model_role=cast(ModelRole, self._model_role), logger=self._logger, tracer=self._tracer, meter=self._meter, ) case _: raise ValueError(f"Unsupported model tier: {self._model_tier}") @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: match hints.get("model_size", ModelSize.AUTO): case ModelSize.AUTO | ModelSize.LARGE: return BisonEmbedding(self._logger, self._tracer, self._meter) case _: return JackalEmbedding(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/fireworks_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time import os import tiktoken import jsonfinder # type: ignore from pydantic import ValidationError from fireworks.client import AsyncFireworks # type: ignore from typing import Any, Mapping from typing_extensions import override from fireworks.client.error import RateLimitError # type: ignore from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import JinaAIEmbedder from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.embedding import Embedder from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.tokenization import EstimatingTokenizer RATE_LIMIT_ERROR_MESSAGE = ( "Fireworks API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Fireworks account balance and billing status.\n" "- Review your API usage limits in Fireworks dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://fireworks.ai/docs/guides/quotas_usage/rate-limits" ) class FireworksEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) + 36 class FireworksSchematicGenerator(BaseSchematicGenerator[T]): supported_hints = ["temperature", "max_tokens"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncFireworks(api_key=os.environ.get("FIREWORKS_API_KEY")) self._tokenizer = FireworksEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return self.model_name @property @override def tokenizer(self) -> FireworksEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( Exception, # Will handle specific Fireworks exceptions ), max_exceptions=3, wait_times=(1.0, 2.0, 4.0), ) ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Fireworks LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() fireworks_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} t_start = time.time() try: response = self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, response_format={ "type": "json_schema", "json_schema": { "schema": self.schema.model_json_schema(), "name": self.schema.__name__, "strict": True, }, }, **fireworks_api_arguments, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise t_end = time.time() if response.usage: # type: ignore self.logger.trace(f"Usage: {response.usage.model_dump_json(indent=2)}") # type: ignore raw_content = response.choices[0].message.content or "{}" # type: ignore try: json_content = normalize_json_output(raw_content) json_object = jsonfinder.only_json(json_content)[2] except Exception: self.logger.error( f"Failed to extract JSON returned by {self.model_name}:\n{raw_content}" ) raise try: model_content = self.schema.model_validate(json_object) await record_llm_metrics( self.meter, self.model_name, input_tokens=response.usage.prompt_tokens, # type: ignore output_tokens=response.usage.completion_tokens, # type: ignore ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, # type: ignore output_tokens=response.usage.completion_tokens, # type: ignore extra={}, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class FireworksLlama3_1_8B(FireworksSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="accounts/fireworks/models/llama-v3p1-8b-instruct", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 128 * 1024 @property @override def tokenizer(self) -> FireworksEstimatingTokenizer: return self._tokenizer class FireworksLlama3_1_70B(FireworksSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="accounts/fireworks/models/llama-v3p1-70b-instruct", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 128 * 1024 @property @override def tokenizer(self) -> FireworksEstimatingTokenizer: return self._tokenizer class FireworksLlama3_1_405B(FireworksSchematicGenerator[T]): """ @warn: This is an extremely large model (405B parameters). Only suitable for high-performance workloads with significant budget considerations. """ def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="accounts/fireworks/models/llama-v3p1-405b-instruct", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 128 * 1024 @property @override def tokenizer(self) -> FireworksEstimatingTokenizer: return self._tokenizer class FireworksMythoMax(FireworksSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="accounts/fireworks/models/mythomax-l2-13b", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 4096 @property @override def tokenizer(self) -> FireworksEstimatingTokenizer: return self._tokenizer class FireworksGemma2_9B(FireworksSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="accounts/fireworks/models/gemma2-9b-it", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 8192 @property @override def tokenizer(self) -> FireworksEstimatingTokenizer: return self._tokenizer class CustomFireworksSchematicGenerator(FireworksSchematicGenerator[T]): """Generic Fireworks generator that accepts any model name.""" def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name=model_name, logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 8192 # Default conservative limit @property @override def tokenizer(self) -> FireworksEstimatingTokenizer: return self._tokenizer # Using JinaAIEmbedder for embeddings since Fireworks focuses on inference class FireworksService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" required_vars = { "FIREWORKS_API_KEY": "", "FIREWORKS_MODEL": "accounts/fireworks/models/llama-v3p1-8b-instruct", } missing_vars = [] for var_name, default_value in required_vars.items(): if not os.environ.get(var_name): if default_value: missing_vars.append(f'export {var_name}="{default_value}"') else: missing_vars.append(f'export {var_name}=""') if missing_vars: return f"""\ You're using the Fireworks NLP service, but the following environment variables are not set: {chr(10).join(missing_vars)} Please set these environment variables before running Parlant. You can get your API key from: https://app.fireworks.ai/settings/users/api-keys """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self._model_name = os.environ.get( "FIREWORKS_MODEL", "accounts/fireworks/models/llama-v3p1-8b-instruct" ) self._embedding_model = os.environ.get( # Need to be implemented "FIREWORKS_EMBEDDING_MODEL", "accounts/fireworks/models/qwen3-embedding-8b" ) self._logger = logger self._tracer = tracer self._meter = meter self._logger.info(f"Initialized FireworksService with {self._model_name}") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") def _get_specialized_generator_class( self, model_name: str, schema_type: type[T], ) -> type[FireworksSchematicGenerator[T]] | None: model_to_class: dict[str, type[FireworksSchematicGenerator[T]]] = { "accounts/fireworks/models/llama-v3p1-8b-instruct": FireworksLlama3_1_8B[schema_type], # type: ignore "accounts/fireworks/models/llama-v3p1-70b-instruct": FireworksLlama3_1_70B[schema_type], # type: ignore "accounts/fireworks/models/llama-v3p1-405b-instruct": FireworksLlama3_1_405B[ schema_type # type: ignore ], "accounts/fireworks/models/mythomax-l2-13b": FireworksMythoMax[schema_type], # type: ignore "accounts/fireworks/models/gemma2-9b-it": FireworksGemma2_9B[schema_type], # type: ignore } return model_to_class.get(model_name) def _log_model_warnings(self, model_name: str) -> None: """Log warnings for resource-intensive models.""" if "405b" in model_name.lower(): self._logger.warning( f"Using {model_name} - This is an extremely large model with significant cost implications. " "Consider using smaller models for development and testing." ) @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> FireworksSchematicGenerator[T]: """Get a schematic generator for the specified type.""" self._log_model_warnings(self._model_name) specialized_class = self._get_specialized_generator_class(self._model_name, schema_type=t) if specialized_class: self._logger.debug(f"Using specialized generator for model: {self._model_name}") return specialized_class( model_name=self._model_name, logger=self._logger, tracer=self._tracer, meter=self._meter, ) else: self._logger.debug(f"Using custom generator for model: {self._model_name}") return CustomFireworksSchematicGenerator[t]( # type: ignore model_name=self._model_name, logger=self._logger, tracer=self._tracer, meter=self._meter, ) @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return JinaAIEmbedder(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: """Fireworks doesn't provide moderation services, so we use no moderation.""" return NoModeration() MODEL_RECOMMENDATIONS = { "accounts/fireworks/models/llama-v3p1-8b-instruct": "Fast and cost-effective for most use cases", "accounts/fireworks/models/llama-v3p1-70b-instruct": "High accuracy for complex reasoning tasks", "accounts/fireworks/models/llama-v3p1-405b-instruct": "@warn: Extremely expensive, use only for critical workloads", "accounts/fireworks/models/gemma2-9b-it": "Good balance of speed and accuracy", "accounts/fireworks/models/mythomax-l2-13b": "Creative writing and roleplay scenarios", } ================================================ FILE: src/parlant/adapters/nlp/gemini_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import enum import inspect import os import time import types from google.api_core.exceptions import NotFound, TooManyRequests, ResourceExhausted, ServerError import google.genai # type: ignore import google.genai.types # type: ignore from collections.abc import Mapping as MappingABC, Sequence as SequenceABC from typing import Any, Literal, Mapping, Sequence, Union, cast from typing_extensions import get_args, get_origin, override from pydantic import BaseModel, Field, ValidationError from pydantic.fields import FieldInfo from parlant.core.common import DefaultBaseModel from parlant.adapters.nlp.common import record_llm_metrics from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.service import ( EmbedderHints, ModelSize, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, FallbackSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger from parlant.core.tracer import Tracer RATE_LIMIT_ERROR_MESSAGE = ( "Google API rate limit exceeded.\n\n" "Possible reasons:\n" "1. Insufficient API credits in your account.\n" "2. Using a free-tier account with limited request capacity.\n" "3. Exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Google API account balance and billing status.\n" "- Review your API usage limits in the Google Cloud Console.\n" "- Learn more about quotas and limits:\n" " https://cloud.google.com/docs/quota-and-billing/quotas/quotas-overview" ) class GoogleEstimatingTokenizer(EstimatingTokenizer): def __init__(self, client: google.genai.Client, model_name: str) -> None: self._client = client self._model_name = model_name @override async def estimate_token_count(self, prompt: str) -> int: model_approximation = { "gemini-embedding-001": "gemini-2.5-flash", }.get(self._model_name, self._model_name) result = await self._client.aio.models.count_tokens( model=model_approximation, contents=prompt, ) return int(result.total_tokens or 0) class GeminiSchematicGenerator(BaseSchematicGenerator[T]): supported_hints = ["temperature", "thinking_config"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = google.genai.Client(api_key=os.environ.get("GEMINI_API_KEY")) self._tokenizer = GoogleEstimatingTokenizer(client=self._client, model_name=self.model_name) @property @override def id(self) -> str: return f"google/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( NotFound, TooManyRequests, ResourceExhausted, ) ), retry(ServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Gemini LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() gemini_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} fd = self._get_schema_function_declaration() config = google.genai.types.GenerateContentConfig( tools=[google.genai.types.Tool(function_declarations=[fd])], tool_config=google.genai.types.ToolConfig( function_calling_config=google.genai.types.FunctionCallingConfig( mode=google.genai.types.FunctionCallingConfigMode.ANY, allowed_function_names=[fd.name], ) ), **gemini_api_arguments, # type: ignore ) t_start = time.time() try: response = await self._client.aio.models.generate_content( model=self.model_name, contents=prompt, config=config, ) except TooManyRequests: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise t_end = time.time() assert response.candidates assert response.candidates[0].content assert response.candidates[0].content.parts assert response.candidates[0].content.parts[0].function_call assert response.candidates[0].content.parts[0].function_call.args json_result = ( response.candidates[0].content.parts[0].function_call.args.get("log_data", {}) or {} ) if response.usage_metadata: self.logger.trace(response.usage_metadata.model_dump_json(indent=2)) try: model_content = self.schema.model_validate(json_result) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage_metadata.prompt_token_count or 0 if response.usage_metadata else 0, output_tokens=response.usage_metadata.candidates_token_count or 0 if response.usage_metadata else 0, cached_input_tokens=response.usage_metadata.cached_content_token_count or 0 if response.usage_metadata else 0, ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage_metadata.prompt_token_count or 0, output_tokens=response.usage_metadata.candidates_token_count or 0, extra={ "cached_input_tokens": ( response.usage_metadata.cached_content_token_count or 0 if response.usage_metadata else 0 ) or 0 }, ) if response.usage_metadata else UsageInfo(input_tokens=0, output_tokens=0, extra={}), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{json_result}" ) raise def _get_schema_function_declaration(self) -> google.genai.types.FunctionDeclaration: # Create a signature from parameters sig = inspect.Signature( parameters=[ inspect.Parameter( name="log_data", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=convert_model_to_gemini_compatible_schema(self.schema), ) ], return_annotation=bool, ) # Create a fake callable def log_data() -> None: pass # Attach the signature log_data.__signature__ = sig # type: ignore fd = google.genai.types.FunctionDeclaration.from_callable( callable=log_data, client=self._client, # type: ignore ) return fd class Gemini_2_0_Flash(GeminiSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gemini-2.0-flash", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 1024 * 1024 class Gemini_2_0_Flash_Lite(GeminiSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gemini-2.0-flash-lite-preview-02-05", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 1024 * 1024 class Gemini_2_5_Flash(GeminiSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gemini-2.5-flash", logger=logger, tracer=tracer, meter=meter, ) @override async def generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: return await super().generate( prompt, {"thinking_config": {"thinking_budget": 0}, **hints}, ) @property @override def max_tokens(self) -> int: return 1024 * 1024 class Gemini_2_5_Flash_Lite(GeminiSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gemini-2.5-flash-lite", logger=logger, tracer=tracer, meter=meter, ) @override async def generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: return await super().generate( prompt, {"thinking_config": {"thinking_budget": 0}, **hints}, ) @property @override def max_tokens(self) -> int: return 1024 * 1024 class Gemini_2_5_Pro(GeminiSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gemini-2.5-pro", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 1024 * 1024 class GoogleEmbedder(BaseEmbedder): supported_hints = ["title", "task_type"] def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(logger, tracer, meter, model_name) self._client = google.genai.Client(api_key=os.environ.get("GEMINI_API_KEY")) self._tokenizer = GoogleEstimatingTokenizer(client=self._client, model_name=self.model_name) @property @override def id(self) -> str: return f"google/{self.model_name}" @property @override def tokenizer(self) -> GoogleEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( NotFound, TooManyRequests, ResourceExhausted, ) ), retry(ServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: gemini_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} try: response = await self._client.aio.models.embed_content( # type: ignore model=self.model_name, contents=texts, # type: ignore config=cast(google.genai.types.EmbedContentConfigDict, gemini_api_arguments), ) except TooManyRequests: self.logger.error( ( "Google API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Google API account balance and billing status.\n" "- Review your API usage limits in Google's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://cloud.google.com/docs/quota-and-billing/quotas/quotas-overview" ), ) raise vectors = [ data_point.values for data_point in response.embeddings or [] if data_point.values ] return EmbeddingResult(vectors=vectors) class GeminiTextEmbedding_001(GoogleEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gemini-embedding-001", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 2048 @property def dimensions(self) -> int: return 3072 class GeminiService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("GEMINI_API_KEY"): return """\ You're using the GEMINI NLP service, but GEMINI_API_KEY is not set. Please set GEMINI_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.logger = logger self._tracer = tracer self._meter = meter self.logger.info("Initialized GeminiService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> GeminiSchematicGenerator[T]: match hints.get("model_size", ModelSize.AUTO): case ModelSize.NANO: return Gemini_2_5_Flash_Lite[t](self.logger, self._tracer, self._meter) # type: ignore case ModelSize.MINI: return Gemini_2_5_Flash[t](self.logger, self._tracer, self._meter) # type: ignore case ModelSize.LARGE: return Gemini_2_5_Pro[t](self.logger, self._tracer, self._meter) # type: ignore case _: return FallbackSchematicGenerator[t]( # type: ignore Gemini_2_5_Flash[t](self.logger, self._tracer, self._meter), # type: ignore Gemini_2_5_Pro[t](self.logger, self._tracer, self._meter), # type: ignore logger=self.logger, ) @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return GeminiTextEmbedding_001(self.logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() def convert_type_annotation_to_gemini_compatible_schema(annotation: Any) -> Any: origin = get_origin(annotation) # If not a generic type, check if it's a BaseModel or Enum if origin is None: # If it's an Enum class, convert to Literal of its values if inspect.isclass(annotation) and issubclass(annotation, enum.Enum): enum_values = tuple(member.value for member in annotation) if len(enum_values) == 1: return Literal[enum_values[0]] return Literal.__getitem__(enum_values) # If it's a BaseModel class, recursively convert it if inspect.isclass(annotation) and issubclass(annotation, DefaultBaseModel): return convert_model_to_gemini_compatible_schema(annotation) return annotation # Get the type arguments args = get_args(annotation) # Convert nested types recursively converted_args = tuple(convert_type_annotation_to_gemini_compatible_schema(arg) for arg in args) # Check if origin is Mapping or Sequence if origin is Mapping or origin is MappingABC: return dict[converted_args] if converted_args else dict # type: ignore if origin is Sequence or origin is SequenceABC: return list[converted_args] if converted_args else list # type: ignore # Handle UnionType (X | Y syntax) - not subscriptable! if origin is types.UnionType: return Union[converted_args] # For other generic types, preserve the origin with converted args if converted_args: return origin[converted_args] return annotation def convert_model_to_gemini_compatible_schema(model_cls: type[DefaultBaseModel]) -> type[BaseModel]: """ Create a new BaseModel class with converted annotations. Returns a new class without modifying the original. """ # Avoid infinite recursion - check if already converted if hasattr(model_cls, "_conversion_cache"): return cast(type[BaseModel], model_cls._conversion_cache) # Build new annotations new_annotations = {} new_fields = {} for field_name, field_info in model_cls.model_fields.items(): # Convert the annotation converted_annotation = convert_type_annotation_to_gemini_compatible_schema( field_info.annotation ) new_annotations[field_name] = converted_annotation # Preserve field metadata (default, description, etc.) # We need to recreate the field with the new annotation field_kwargs = {} if field_info.default is not None and field_info.default is not FieldInfo: field_kwargs["default"] = field_info.default elif field_info.default_factory is not None: field_kwargs["default_factory"] = field_info.default_factory if field_info.description is not None: field_kwargs["description"] = field_info.description if field_info.title is not None: field_kwargs["title"] = field_info.title if field_info.examples is not None: field_kwargs["examples"] = field_info.examples # Add other field properties as needed if field_kwargs: new_fields[field_name] = Field(**field_kwargs) # Create new model class new_model_attrs = {"__annotations__": new_annotations, **new_fields} # Preserve model config if present if hasattr(model_cls, "model_config"): new_model_attrs["model_config"] = model_cls.model_config # Create the new class converted_model = type(f"{model_cls.__name__}Converted", (DefaultBaseModel,), new_model_attrs) # Cache the conversion to avoid infinite recursion setattr(model_cls, "_conversion_cache", converted_model) return converted_model ================================================ FILE: src/parlant/adapters/nlp/glm_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import time from openai import ( APIConnectionError, APIResponseValidationError, APITimeoutError, AsyncClient, ConflictError, InternalServerError, RateLimitError, ) from typing import Any, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( ModerationService, NoModeration, ) from parlant.core.tracer import Tracer RATE_LIMIT_ERROR_MESSAGE = """\ GLM API rate limit exceeded. Possible reasons: 1. Your account may have insufficient API credits. 2. You may be using a free-tier account with limited request capacity. 3. You might have exceeded the requests-per-minute limit for your account. Recommended actions: - Check your GLM account balance and billing status. - Review your API usage limits in GLM's dashboard. - For more details on rate limits and usage tiers, visit: https://docs.bigmodel.cn/cn/faq/api-code """ class GLMEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class GLMEmbedder(BaseEmbedder): supported_arguments = ["dimensions"] def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient( base_url="https://open.bigmodel.cn/api/paas/v4", api_key=os.environ["GLM_API_KEY"], ) self._tokenizer = GLMEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"glm/{self.model_name}" @property @override def tokenizer(self) -> GLMEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: filtered_hints = {k: v for k, v in hints.items() if k in self.supported_arguments} try: response = await self._client.embeddings.create( model=self.model_name, input=texts, **filtered_hints, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise vectors = [data_point.embedding for data_point in response.data] return EmbeddingResult(vectors=vectors) class GMLTextEmbedding_3(GLMEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="embedding-3", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 8000 @property def dimensions(self) -> int: return 2048 class GLMSchematicGenerator(BaseSchematicGenerator[T]): supported_glm_params = ["temperature", "max_tokens"] supported_hints = supported_glm_params + ["strict"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient( base_url="https://open.bigmodel.cn/api/paas/v4", api_key=os.environ["GLM_API_KEY"], ) self._tokenizer = GLMEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"GLM/{self.model_name}" @property @override def tokenizer(self) -> GLMEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"GLM LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() glm_api_arguments = {k: v for k, v in hints.items() if k in self.supported_glm_params} t_start = time.time() response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, max_tokens=4096, response_format={"type": "json_object"}, **glm_api_arguments, ) t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.choices[0].message.content or "{}" try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) assert response.usage await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, cached_input_tokens=getattr( response, "usage.prompt_cache_hit_tokens", 0, ), ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={ "cached_input_tokens": getattr( response, "usage.prompt_cache_hit_tokens", 0, ) }, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class GLM_4_5(GLMSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="glm-4.5", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 96 * 1024 class GLMService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("GLM_API_KEY"): return """\ You're using the GLM NLP service, but GLM_API_KEY is not set. Please set GLM_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.logger = logger self._tracer = tracer self._meter = meter self.logger.info("Initialized GLMService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> GLMSchematicGenerator[T]: return GLM_4_5[t](self.logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return GMLTextEmbedding_3(logger=self.logger, tracer=self._tracer, meter=self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/hugging_face.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from collections.abc import Mapping import os from pathlib import Path from typing import Any from typing_extensions import override import torch # type: ignore from typing import cast from transformers import AutoModel, AutoTokenizer, PreTrainedTokenizerBase, PreTrainedModel # type: ignore from huggingface_hub.errors import ( # type: ignore InferenceTimeoutError, InferenceEndpointError, InferenceEndpointTimeoutError, TextGenerationError, ) from tempfile import gettempdir from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.embedding import BaseEmbedder, EmbeddingResult _TOKENIZER_MODELS: dict[str, PreTrainedTokenizerBase] = {} _AUTO_MODELS: dict[str, PreTrainedModel] = {} _DEVICE: torch.device | None = None def _model_temp_dir() -> str: return str(Path(gettempdir()) / "parlant_data" / "hf_models") def _create_tokenizer(model_name: str) -> PreTrainedTokenizerBase: if model_name in _TOKENIZER_MODELS: return _TOKENIZER_MODELS[model_name] save_dir = os.environ.get("PARLANT_HOME", _model_temp_dir()) os.makedirs(save_dir, exist_ok=True) tokenizer: PreTrainedTokenizerBase = AutoTokenizer.from_pretrained( model_name, trust_remote_code=True ) # type: ignore tokenizer.save_pretrained(save_dir) _TOKENIZER_MODELS[model_name] = tokenizer return tokenizer def _get_device() -> torch.device: global _DEVICE if _DEVICE: return _DEVICE if torch.backends.mps.is_available(): _DEVICE = torch.device("mps") elif torch.cuda.is_available(): _DEVICE = torch.device("cuda") else: _DEVICE = torch.device("cpu") return _DEVICE def _create_auto_model(model_name: str) -> PreTrainedModel: if model_name in _AUTO_MODELS: return _AUTO_MODELS[model_name] save_dir = os.environ.get("PARLANT_HOME", _model_temp_dir()) os.makedirs(save_dir, exist_ok=True) model = AutoModel.from_pretrained( pretrained_model_name_or_path=model_name, attn_implementation="eager", trust_remote_code=True, ).to(_get_device()) model = cast(PreTrainedModel, model) model.save_pretrained(save_dir) model.eval() # type: ignore _AUTO_MODELS[model_name] = model return model class HuggingFaceEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self._tokenizer = _create_tokenizer(model_name) @override async def estimate_token_count(self, prompt: str) -> int: # Use encode to get token ids, which is always available tokens = self._tokenizer.encode(prompt) return len(tokens) class HuggingFaceEmbedder(BaseEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter, model_name: str) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._model = _create_auto_model(model_name) self._tokenizer = HuggingFaceEstimatingTokenizer(model_name=model_name) @property @override def id(self) -> str: return f"hugging-face/{self.model_name}" @property @override def max_tokens(self) -> int: return 8192 @property @override def tokenizer(self) -> HuggingFaceEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( InferenceTimeoutError, InferenceEndpointError, InferenceEndpointTimeoutError, ), max_exceptions=2, ), retry(exceptions=(TextGenerationError), max_exceptions=3), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: tokenized_texts = self._tokenizer._tokenizer.batch_encode_plus( texts, padding=True, truncation=True, return_tensors="pt" ) tokenized_texts = {key: value.to(_get_device()) for key, value in tokenized_texts.items()} with torch.no_grad(): embeddings = self._model(**tokenized_texts).last_hidden_state[:, 0, :] return EmbeddingResult(vectors=embeddings.tolist()) class JinaAIEmbedder(HuggingFaceEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( logger=logger, meter=meter, tracer=tracer, model_name="jinaai/jina-embeddings-v2-base-en", ) @property @override def dimensions(self) -> int: return 768 ================================================ FILE: src/parlant/adapters/nlp/lakera.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from itertools import chain import os from typing_extensions import override import httpx from parlant.core.loggers import Logger from parlant.core.nlp.moderation import ( CustomerModerationContext, ModerationCheck, BaseModerationService, ModerationTag, ) from parlant.core.meter import Meter class LakeraGuard(BaseModerationService): def __init__(self, logger: Logger, meter: Meter) -> None: super().__init__(logger, meter) @override async def do_moderate(self, context: CustomerModerationContext) -> ModerationCheck: api_key: str | None = os.environ.get("LAKERA_API_KEY") if not api_key: self.logger.warning( "LakeraGuard is enabled but LAKERA_API_KEY is missing. Skipping check..." ) return ModerationCheck(flagged=False, tags=[]) def extract_tags(category: str) -> list[ModerationTag]: mapping: dict[str, list[ModerationTag]] = { "moderated_content_crime": ["illicit"], "moderated_content_hate": ["hate"], "moderated_content_profanity": ["harassment"], "moderated_content_sexual": ["sexual"], "moderated_content_violence": ["violence"], "prompt_attack": ["jailbreak"], } return mapping.get(category.replace("/", "_").replace("-", "_"), []) with self.logger.scope("Lakera Moderation Request"): async with httpx.AsyncClient(follow_redirects=True, timeout=30) as client: response = await client.post( "https://api.lakera.ai/v2/guard/results", json={"messages": [{"content": context.message, "role": "user"}]}, headers={"Authorization": f"Bearer {api_key}"}, ) if response.is_error: raise Exception("Moderation service failure (Lakera Guard)") data = response.json() results = [ ( r["detector_type"], { "l1_confident": True, "l2_very_likely": True, "l3_likely": True, "l4_less_likely": False, "l5_unlikely": False, }.get(r["result"], False), ) for r in data["results"] ] return ModerationCheck( flagged=any(detected for _category, detected in results), tags=list( set( chain.from_iterable( extract_tags(category) for category, detected in results if detected ) ) ), ) ================================================ FILE: src/parlant/adapters/nlp/litellm_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import time from typing import Any, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken import litellm from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import JinaAIEmbedder from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( ModerationService, NoModeration, ) RATE_LIMIT_ERROR_MESSAGE = ( "LiteLLM to provider API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your LLM Provider account balance and billing status.\n" "- Review your API usage limits in Provider's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " Your Provider's API documentation." ) class LiteLLMEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class LiteLLMSchematicGenerator(BaseSchematicGenerator[T]): supported_litellm_params = [ "temperature", "max_tokens", "logit_bias", "adapter_id", "adapter_source", ] supported_hints = supported_litellm_params + ["strict"] def __init__( self, base_url: str | None, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self.base_url = base_url self._client = litellm self._tokenizer = LiteLLMEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"litellm/{self.model_name}" @property @override def tokenizer(self) -> LiteLLMEstimatingTokenizer: return self._tokenizer @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() litellm_api_arguments = { k: v for k, v in hints.items() if k in self.supported_litellm_params } # Only pass api_key if explicitly set; otherwise let LiteLLM auto-detect # provider-specific keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) api_key = os.environ.get("LITELLM_PROVIDER_API_KEY") t_start = time.time() response = await self._client.acompletion( base_url=self.base_url, api_key=api_key, messages=[{"role": "user", "content": prompt}], model=self.model_name, max_tokens=5000, response_format={"type": "json_object"}, **litellm_api_arguments, ) t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.choices[0].message.content or "{}" try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning( f"Invalid JSON returned by litellm/{self.model_name}:\n{raw_content})" ) json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) assert response.usage await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, cached_input_tokens=getattr( response, "usage.prompt_cache_hit_tokens", 0, ), ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={ "cached_input_tokens": getattr( response, "usage.prompt_cache_hit_tokens", 0, ) }, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by litellm/{self.model_name} does not match expected schema:\n{raw_content}" ) raise class LiteLLM_Default(LiteLLMSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str | None, model_name: str ) -> None: super().__init__( base_url=base_url, model_name=model_name, logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 5000 # 8192 16381 class LiteLLMEmbedder(BaseEmbedder): """Embedder that uses LiteLLM to access various embedding providers.""" def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, base_url: str | None = None, ) -> None: super().__init__(logger, tracer, meter, model_name) self._base_url = base_url self._client = litellm self._tokenizer = LiteLLMEstimatingTokenizer(model_name=model_name) @property @override def id(self) -> str: return f"litellm/{self.model_name}" @property @override def tokenizer(self) -> LiteLLMEstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: return int(os.environ.get("LITELLM_EMBEDDING_MAX_TOKENS", 8192)) @property @override def dimensions(self) -> int: return int(os.environ.get("LITELLM_EMBEDDING_DIMENSIONS", 1536)) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: api_key = os.environ.get("LITELLM_PROVIDER_API_KEY") response = await self._client.aembedding( model=self.model_name, input=texts, api_key=api_key, api_base=self._base_url, ) vectors = [data["embedding"] for data in response.data] return EmbeddingResult(vectors=vectors) class LiteLLMService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("LITELLM_PROVIDER_MODEL_NAME"): return """\ You're using the LITELLM NLP service, but LITELLM_PROVIDER_MODEL_NAME is not set. Please set LITELLM_PROVIDER_MODEL_NAME in your environment before running Parlant. """ # Note: LITELLM_PROVIDER_API_KEY is optional. If not set, LiteLLM will # auto-detect provider-specific keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) return None def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: self._base_url = os.environ.get("LITELLM_PROVIDER_BASE_URL") self._model_name = os.environ["LITELLM_PROVIDER_MODEL_NAME"] self._embedding_model_name = os.environ.get("LITELLM_EMBEDDING_MODEL_NAME") self.logger = logger self._tracer = tracer self._meter = meter log_msg = f"Initialized LiteLLMService with {self._model_name}" if self._embedding_model_name: log_msg += f" (embeddings: {self._embedding_model_name})" if self._base_url: log_msg += f" at {self._base_url}" self.logger.info(log_msg) @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> LiteLLMSchematicGenerator[T]: return LiteLLM_Default[t]( # type: ignore self.logger, self._tracer, self._meter, self._base_url, self._model_name ) def create_embedder(self) -> Embedder: if self._embedding_model_name: return LiteLLMEmbedder( model_name=self._embedding_model_name, logger=self.logger, tracer=self._tracer, meter=self._meter, base_url=self._base_url, ) return JinaAIEmbedder(self.logger, self._tracer, self._meter) @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return self.create_embedder() @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/mistral_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import time from typing import Any, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.canned_response_generator import CannedResponseSelectionSchema from parlant.core.engines.alpha.guideline_matching.generic.disambiguation_batch import ( DisambiguationGuidelineMatchesSchema, ) from parlant.core.engines.alpha.guideline_matching.generic.journey.journey_backtrack_node_selection import ( JourneyBacktrackNodeSelectionSchema, ) from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( BaseModerationService, CustomerModerationContext, ModerationCheck, ModerationService, ModerationTag, ) try: from mistralai import Mistral from mistralai.models import SDKError, HTTPValidationError except ImportError: Mistral = None # type: ignore SDKError = Exception # type: ignore HTTPValidationError = Exception # type: ignore RATE_LIMIT_ERROR_MESSAGE = ( "Mistral AI API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Mistral AI account balance and billing status.\n" "- Review your API usage limits in Mistral AI's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://docs.mistral.ai/api/\n" ) class MistralEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name # Use GPT-4o encoding as approximation for Mistral models self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class MistralSchematicGenerator(BaseSchematicGenerator[T]): supported_mistral_params = ["temperature", "max_tokens"] supported_hints = supported_mistral_params def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) self._tokenizer = MistralEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"mistral/{self.model_name}" @property @override def tokenizer(self) -> MistralEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( ConnectionError, TimeoutError, SDKError, HTTPValidationError, ), ), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Mistral LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() mistral_api_arguments = { k: v for k, v in hints.items() if k in self.supported_mistral_params } t_start = time.time() try: response = await self._client.chat.complete_async( messages=[{"role": "user", "content": prompt}], # type: ignore[arg-type] model=self.model_name, response_format={"type": "json_object"}, # type: ignore[arg-type] **mistral_api_arguments, ) except SDKError as e: if "rate" in str(e).lower() or "429" in str(e): self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise t_end = time.time() if response.usage: self.logger.trace( f"Usage: input_tokens={response.usage.prompt_tokens}, " f"output_tokens={response.usage.completion_tokens}" ) raw_content = response.choices[0].message.content or "{}" try: # Convert content to string if needed content_str = raw_content if isinstance(raw_content, str) else str(raw_content) json_content = json.loads(normalize_json_output(content_str)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) assert response.usage await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens or 0, output_tokens=response.usage.completion_tokens or 0, cached_input_tokens=0, ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens or 0, output_tokens=response.usage.completion_tokens or 0, ), ), ) except ValidationError as e: self.logger.error( f"Error: {e.json(indent=2)}\nJSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class Mistral_Large_2411(MistralSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="mistral-large-2411", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class Mistral_Medium_2508(MistralSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="mistral-medium-2508", logger=logger, tracer=tracer, meter=meter ) @property @override def max_tokens(self) -> int: return 128 * 1024 class Mistral_Small_2506(MistralSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="mistral-small-2506", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class MistralEmbedder(BaseEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name="mistral-embed") self._client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) self._tokenizer = MistralEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"mistral/{self.model_name}" @property @override def tokenizer(self) -> MistralEstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 1024 @policy( [ retry( exceptions=( ConnectionError, TimeoutError, SDKError, HTTPValidationError, ), ), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: try: response = await self._client.embeddings.create_async( model=self.model_name, inputs=texts, ) except SDKError as e: if "rate" in str(e).lower() or "429" in str(e): self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise vectors = [ data_point.embedding if data_point.embedding else [] for data_point in response.data ] return EmbeddingResult(vectors=vectors) class MistralModerationService(BaseModerationService): def __init__(self, logger: Logger, meter: Meter) -> None: super().__init__(logger=logger, meter=meter) self.model_name = "mistral-moderation-2411" self._client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) @override async def do_moderate(self, context: CustomerModerationContext) -> ModerationCheck: def extract_tags(category: str) -> list[ModerationTag]: mapping: dict[str, list[ModerationTag]] = { "sexual": ["sexual"], "hate_and_discrimination": ["hate"], "violence_and_threats": ["violence"], "dangerous_and_criminal_content": ["illicit"], "selfharm": ["self-harm"], "health": ["illicit"], "financial": ["illicit"], "law": ["illicit"], "pii": ["illicit"], } return mapping.get(category.replace("-", "_").replace(" ", "_").lower(), []) response = await self._client.classifiers.moderate_chat_async( model=self.model_name, inputs=[{"role": "user", "content": context.message}], # type: ignore[arg-type] ) result = response.results[0] flagged = False all_tags: list[ModerationTag] = [] if result.categories: for category_result in result.categories: # Type check since the API may return different formats if hasattr(category_result, "category_scores") and category_result.category_scores: # Check if any score indicates flagged content (threshold can be adjusted) for score_item in category_result.category_scores: if ( hasattr(score_item, "score") and score_item.score and score_item.score > 0.5 ): flagged = True if hasattr(category_result, "category"): all_tags.extend(extract_tags(str(category_result.category))) break return ModerationCheck( flagged=flagged, tags=list(set(all_tags)), ) class MistralService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("MISTRAL_API_KEY"): return """\ You're using the Mistral NLP service, but MISTRAL_API_KEY is not set. Please set MISTRAL_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self._logger = logger self._tracer = tracer self._meter = meter self._logger.info("Initialized MistralService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> MistralSchematicGenerator[T]: if ( t == JourneyBacktrackNodeSelectionSchema or t == DisambiguationGuidelineMatchesSchema or t == CannedResponseSelectionSchema ): return Mistral_Large_2411[t](self._logger, self._tracer, self._meter) # type: ignore return Mistral_Medium_2508[t](self._logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return MistralEmbedder(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return MistralModerationService(self._logger, self._meter) ================================================ FILE: src/parlant/adapters/nlp/modelscope_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Maintainer: Rongkun Yan <2493404415@qq.com> from __future__ import annotations import time from openai import ( APIConnectionError, APIResponseValidationError, APITimeoutError, AsyncClient, ConflictError, InternalServerError, RateLimitError, ) from typing import Any, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import JinaAIEmbedder from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import Embedder from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( ModerationService, NoModeration, ) class ModelScopeEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class ModelScopeSchematicGenerator(BaseSchematicGenerator[T]): supported_modelscope_params = ["temperature", "logit_bias", "max_tokens"] supported_hints = supported_modelscope_params + ["strict"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient( base_url="https://api-inference.modelscope.cn/v1", api_key=os.environ["MODELSCOPE_API_KEY"], ) self._tokenizer = ModelScopeEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"modelscope/{self.model_name}" @property @override def tokenizer(self) -> ModelScopeEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"ModelScope LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() modelscope_api_arguments = { k: v for k, v in hints.items() if k in self.supported_modelscope_params } t_start = time.time() response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, stream=True, extra_body={"enable_thinking": False}, max_tokens=8192, response_format={"type": "json_object"}, **modelscope_api_arguments, ) t_end = time.time() raw_content = "" async for chunk in response: if chunk.choices and chunk.choices[0].delta and chunk.choices[0].delta.content: raw_content += chunk.choices[0].delta.content try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) input_tokens = await self.tokenizer.estimate_token_count(prompt) output_tokens = await self.tokenizer.estimate_token_count(raw_content) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=input_tokens, output_tokens=output_tokens, cached_input_tokens=0, ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=input_tokens, output_tokens=output_tokens, extra={}, ), ), ) except ValidationError as ve: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) self.logger.error(f"Validation error details: {str(ve)}") raise class ModelScopeChat(ModelScopeSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: model_name = os.environ["MODELSCOPE_MODEL_NAME"] super().__init__(model_name=model_name, logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class ModelScopeService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("MODELSCOPE_MODEL_NAME"): return """\ You're using the ModelScope NLP service, but MODELSCOPE_MODEL_NAME is not set. Please set MODELSCOPE_MODEL_NAME in your environment before running Parlant. """ if not os.environ.get("MODELSCOPE_API_KEY"): return """\ You're using the ModelScope NLP service, but MODELSCOPE_API_KEY is not set. Please set MODELSCOPE_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self._logger = logger self._tracer = tracer self._meter = meter self._logger.info("Initialized ModelScopeService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> ModelScopeSchematicGenerator[T]: return ModelScopeChat[t](self._logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return JinaAIEmbedder(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/ollama_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Maintainer: Agam Dubey import os import time from typing import Any, Callable, Mapping from typing_extensions import override import asyncio import tiktoken import ollama import jsonfinder # type: ignore from pydantic import ValidationError from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger from parlant.core.tracer import Tracer class OllamaError(Exception): """Base exception for Ollama-related errors.""" pass class OllamaConnectionError(OllamaError): """Raised when unable to connect to Ollama server.""" pass class OllamaModelError(OllamaError): """Raised when there are issues with the Ollama model.""" pass class OllamaTimeoutError(OllamaError): """Raised when Ollama request times out.""" pass class OllamaModelVerifier: """Utility class for verifying Ollama model availability.""" @staticmethod def verify_models(base_url: str, generation_model: str, embedding_model: str) -> str | None: """ Returns an error string if required Ollama models are missing, or None if all are available. """ client = ollama.Client(host=base_url.rstrip("/")) try: models = client.list() model_names = [] for model in models.get("models", []): if hasattr(model, "model"): model_names.append(model.model) elif isinstance(model, dict) and "model" in model: model_names.append(model["model"]) elif isinstance(model, dict) and "name" in model: model_names.append(model["name"]) missing_models = [] gen_model_found = any(generation_model in model for model in model_names) if not gen_model_found and generation_model not in model_names: missing_models.append(f" ollama pull {generation_model}") embed_model_found = any(embedding_model in model for model in model_names) if not embed_model_found and embedding_model not in model_names: missing_models.append(f" ollama pull {embedding_model}") if missing_models: return f"""\ The following required models are not available in Ollama: {chr(10).join(missing_models)} Please pull the missing models using the commands above. Available models: {", ".join(model_names) if model_names else "None"} """ return None except ollama.ResponseError as e: if e.status_code in [502, 503, 504]: return f"""\ Cannot connect to Ollama server at {base_url}. Please ensure Ollama is running: ollama serve Or check if the OLLAMA_BASE_URL is correct: {base_url} """ else: return f"Error checking Ollama models: {e.error}" except Exception as e: return f"Error connecting to Ollama: {str(e)}" class OllamaEstimatingTokenizer(EstimatingTokenizer): """Simple tokenizer that estimates token count for Ollama models.""" def __init__(self, model_name: str): self.model_name = model_name self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: """Estimate token count using tiktoken""" tokens = self.encoding.encode(prompt) return int(len(tokens) * 1.15) class OllamaSchematicGenerator(BaseSchematicGenerator[T]): """Schematic generator that uses Ollama models.""" supported_hints = ["temperature", "max_tokens", "top_p", "top_k", "repeat_penalty", "timeout"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434", default_timeout: int | str = 300, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self.base_url = base_url.rstrip("/") self._tokenizer = OllamaEstimatingTokenizer(model_name) self._default_timeout = default_timeout self._client = ollama.AsyncClient(host=base_url) @property @override def id(self) -> str: return f"ollama/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: if "1b" in self.model_name.lower(): return 12288 elif "4b" in self.model_name.lower(): return 16384 elif "8b" in self.model_name.lower(): return 16384 elif "12b" in self.model_name.lower() or "70b" in self.model_name.lower(): return 16384 elif "27b" in self.model_name.lower() or "405b" in self.model_name.lower(): return 32768 else: return 16384 def _create_options(self, hints: Mapping[str, Any]) -> dict[str, Any]: """Create options dict from hints for Ollama.""" options = {} if "temperature" in hints: options["temperature"] = hints["temperature"] if "max_tokens" in hints: options["num_predict"] = hints["max_tokens"] if "top_p" in hints: options["top_p"] = hints["top_p"] if "top_k" in hints: options["top_k"] = hints["top_k"] if "repeat_penalty" in hints: options["repeat_penalty"] = hints["repeat_penalty"] options.setdefault("temperature", 0.3) options.setdefault("top_p", 0.9) options.setdefault("repeat_penalty", 1.1) options.setdefault("num_ctx", self.max_tokens) if "1b" in self.model_name.lower(): options["temperature"] = 0.1 options["top_p"] = 0.5 return options @policy( [ retry( exceptions=(OllamaConnectionError, OllamaTimeoutError, ollama.ResponseError), max_exceptions=3, wait_times=(2.0, 4.0, 8.0), ) ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Ollama LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() timeout = hints.get("timeout", self._default_timeout) options = self._create_options(hints) t_start = time.time() try: self.logger.debug(f"Sending request to Ollama with timeout={timeout}s") response = await asyncio.wait_for( self._client.generate( model=self.model_name, prompt=prompt, format=self.schema.model_json_schema(), options=options, stream=False, ), timeout=timeout, ) except asyncio.TimeoutError: elapsed = time.time() - t_start self.logger.error(f"Ollama request timed out after {elapsed:.1f}s (timeout={timeout}s)") raise OllamaTimeoutError( f"Request timed out after {elapsed:.1f}s. Consider increasing timeout or using a smaller model." ) except ollama.ResponseError as e: if e.status_code == 404: raise OllamaModelError( f"Model {self.model_name} not found. Please pull it first with: ollama pull {self.model_name}" ) elif e.status_code in [502, 503, 504]: raise OllamaConnectionError(f"Cannot connect to Ollama server at {self.base_url}") else: self.logger.error(f"Ollama API error {e.status_code}: {e.error}") raise OllamaError(f"API request failed: {e.error}") except Exception as e: self.logger.error(f"Unexpected error calling Ollama: {e}") raise OllamaConnectionError(f"Unexpected error: {e}") t_end = time.time() raw_content = response.get("response", "") if not raw_content: raise ValueError("No content in response") json_object = None try: normalized = normalize_json_output(raw_content) json_object = jsonfinder.only_json(normalized)[2] except Exception: self.logger.error( f"Failed to extract JSON returned by {self.model_name}:\n{raw_content}" ) raise prompt_eval_count = response.get("prompt_eval_count", 0) eval_count = response.get("eval_count", 0) try: model_content = self.schema.model_validate(json_object) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=prompt_eval_count, output_tokens=eval_count, ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__ if hasattr(self, "schema") else "unknown", model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=prompt_eval_count, output_tokens=eval_count, ), ), ) except ValidationError as e: self.logger.error( f"JSON content from {self.model_name} does not match expected schema. " f"Validation errors: {e.errors()}" ) if "1b" in self.model_name.lower(): self.logger.warning( "The 1B model often struggles with complex schemas. " "Consider using gemma3:4b or larger for better reliability." ) raise class OllamaGemma3_1B(OllamaSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434" ) -> None: super().__init__( model_name="gemma3:1b", logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class OllamaGemma3_4B(OllamaSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434" ) -> None: super().__init__( model_name="gemma3:4b", logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class OllamaGemma3_12B(OllamaSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434" ) -> None: super().__init__( model_name="gemma3:12b", logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class OllamaGemma3_27B(OllamaSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434" ) -> None: super().__init__( model_name="gemma3:27b", logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class OllamaLlama31_8B(OllamaSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434" ) -> None: super().__init__( model_name="llama3.1:8b", logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class OllamaLlama31_70B(OllamaSchematicGenerator[T]): """ @warn: This is a very large model (70B parameters) that requires significant GPU memory. Recommended for use with cloud providers or high-end hardware only. Consider using llama3.1:8b or smaller models for local development. """ def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434" ) -> None: super().__init__( model_name="llama3.1:70b", logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class OllamaLlama31_405B(OllamaSchematicGenerator[T]): """ @warn: This is an extremely large model (405B parameters) that requires massive GPU memory. Only suitable for high-end cloud providers with multiple high-memory GPUs. Not recommended for local use. Consider llama3.1:8b or llama3.1:70b instead. """ def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434" ) -> None: super().__init__( model_name="llama3.1:405b", logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class CustomOllamaSchematicGenerator(OllamaSchematicGenerator[T]): """Generic Ollama generator that accepts any model name.""" def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, base_url: str = "http://localhost:11434", ) -> None: super().__init__( model_name=model_name, logger=logger, tracer=tracer, meter=meter, base_url=base_url, ) class OllamaEmbedder(BaseEmbedder): """Embedder that uses Ollama embedding models.""" supported_arguments = ["dimensions"] def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter): super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self.base_url = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434").rstrip("/") self._tokenizer = OllamaEstimatingTokenizer(self.model_name) self._client = ollama.AsyncClient(host=self.base_url) @property @override def id(self) -> str: return f"ollama/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: return 8192 @policy( [ retry( exceptions=(OllamaConnectionError, ollama.ResponseError), max_exceptions=3, wait_times=(1.0, 2.0, 4.0), ) ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: filtered_hints = {k: v for k, v in hints.items() if k in self.supported_arguments} try: response = await self._client.embed( model=self.model_name, input=texts, **filtered_hints ) vectors = response.get("embeddings", []) return EmbeddingResult(vectors=vectors) except ollama.ResponseError as e: if e.status_code == 404: raise OllamaModelError( f"Embedding model {self.model_name} not found. Please pull it first with: ollama pull {self.model_name}" ) elif e.status_code in [502, 503, 504]: raise OllamaConnectionError(f"Cannot connect to Ollama server at {self.base_url}") else: raise OllamaError(f"Embedding request failed: {e.error}") except Exception as e: self.logger.error(f"Error during embedding: {e}") raise OllamaConnectionError(f"Unexpected error: {e}") class OllamaNomicEmbedding(OllamaEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="nomic-embed-text", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 768 class OllamaMxbiEmbeddingLarge(OllamaEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="mxbai-embed-large", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 1024 class OllamaBgeM3EmbeddingLarge(OllamaEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="bge-m3", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 1024 class OllamaCustomEmbedding(OllamaEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: self.model_name = os.environ.get("OLLAMA_EMBEDDING_MODEL", "nomic-embed-text") self.vector_size = int(os.environ.get("OLLAMA_EMBEDDING_VECTOR_SIZE", "768")) super().__init__(model_name=self.model_name, logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return self.vector_size class OllamaService(NLPService): """NLP Service that uses Ollama models.""" @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" required_vars = { "OLLAMA_BASE_URL": "http://localhost:11434", "OLLAMA_MODEL": "gemma3", "OLLAMA_EMBEDDING_MODEL": "nomic-embed-text", "OLLAMA_API_TIMEOUT": "300", } missing_vars = [] for var_name, default_value in required_vars.items(): if not os.environ.get(var_name): missing_vars.append(f'export {var_name}="{default_value}"') if missing_vars: return f"""\ You're using the Ollama NLP service, but the following environment variables are not set: {chr(10).join(missing_vars)} Please set these environment variables before running Parlant. """ return None @staticmethod def verify_models() -> str | None: """ Verify that the required models are available in Ollama. Returns an error message if models are missing, None if all are available. """ base_url = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434").rstrip("/") embedding_model = os.environ.get("OLLAMA_EMBEDDING_MODEL", "nomic-embed-text") generation_model = os.environ.get("OLLAMA_MODEL", "gemma3:4b") if error := OllamaModelVerifier.verify_models(base_url, generation_model, embedding_model): return f"Model Verification Issue:\n{error}" return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.base_url = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434").rstrip("/") self.model_name = os.environ.get("OLLAMA_MODEL", "gemma3:4b") self.embedding_model = os.environ.get("OLLAMA_EMBEDDING_MODEL", "nomic-embed-text") self.default_timeout = int( os.environ.get("OLLAMA_API_TIMEOUT", 300) ) # always convert to int self.logger = logger self._tracer = tracer self._meter = meter self.logger.info(f"Initialized OllamaService with {self.model_name} at {self.base_url}") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") def _get_specialized_generator_class( self, model_name: str, schema_type: type[T], ) -> Callable[..., OllamaSchematicGenerator[T]] | None: """ Returns the specialized generator class for known models, or None for custom models. """ model_to_class: dict[str, type[OllamaSchematicGenerator[T]]] = { "gemma3:1b": OllamaGemma3_1B[schema_type], # type: ignore "gemma3:4b": OllamaGemma3_4B[schema_type], # type: ignore "gemma3:12b": OllamaGemma3_12B[schema_type], # type: ignore "gemma3:27b": OllamaGemma3_27B[schema_type], # type: ignore "llama3.1:8b": OllamaLlama31_8B[schema_type], # type: ignore "llama3.1:70b": OllamaLlama31_70B[schema_type], # type: ignore "llama3.1:405b": OllamaLlama31_405B[schema_type], # type: ignore } if generator_class := model_to_class.get(model_name): return generator_class else: return None def _log_model_warnings(self, model_name: str) -> None: """Log warnings for resource-intensive models.""" if "70b" in model_name.lower(): self.logger.warning( f"Using {model_name} - This is a very large model requiring significant GPU memory. " "Consider using smaller models for local development." ) elif "405b" in model_name.lower(): self.logger.warning( f"Using {model_name} - This is an extremely large model requiring massive GPU resources. " "Only suitable for high-end cloud providers. Consider smaller alternatives." ) @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> SchematicGenerator[T]: """Get a schematic generator for the specified type.""" self._log_model_warnings(self.model_name) specialized_class = self._get_specialized_generator_class(self.model_name, schema_type=t) if specialized_class: self.logger.debug(f"Using specialized generator for model: {self.model_name}") generator = specialized_class(logger=self.logger, base_url=self.base_url) else: self.logger.debug(f"Using custom generator for model: {self.model_name}") generator = CustomOllamaSchematicGenerator[t]( # type: ignore model_name=self.model_name, logger=self.logger, tracer=self._tracer, meter=self._meter, base_url=self.base_url, ) generator._default_timeout = self.default_timeout return generator @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: if "nomic" in self.embedding_model.lower(): return OllamaNomicEmbedding(self.logger, self._tracer, self._meter) elif "mxbai" in self.embedding_model.lower(): return OllamaMxbiEmbeddingLarge(self.logger, self._tracer, self._meter) elif "bge" in self.embedding_model.lower(): return OllamaBgeM3EmbeddingLarge(self.logger, self._tracer, self._meter) else: # its a custom embedding model return OllamaCustomEmbedding(self.logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: """Get a moderation service (using no moderation for local models).""" return NoModeration() # Model size recommendations MODEL_RECOMMENDATIONS = { "gemma3:1b": "Fast but may struggle with complex schemas", "gemma3:4b": "Recommended for most use cases - good balance of speed and accuracy", "llama3.1:8b": "Better reasoning capabilities", "gemma3:12b": "High accuracy for complex tasks", "gemma3:27b": "Very high accuracy but slower", "llama3.1:70b": "@warn: Requires significant GPU memory (40GB+)", "llama3.1:405b": "@warn: Requires massive GPU resources (200GB+), cloud-only", } ================================================ FILE: src/parlant/adapters/nlp/openai_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations from itertools import chain import re import time from openai import ( APIConnectionError, APIResponseValidationError, APITimeoutError, AsyncClient, ConflictError, InternalServerError, RateLimitError, ) from typing import Any, AsyncIterator, Callable, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.canned_response_generator import ( CannedResponseDraftSchema, CannedResponseSelectionSchema, ) from parlant.core.engines.alpha.guideline_matching.generic.journey.journey_backtrack_check import ( JourneyBacktrackCheckSchema, ) from parlant.core.engines.alpha.guideline_matching.generic.journey.journey_backtrack_node_selection import ( JourneyBacktrackNodeSelectionSchema, ) from parlant.core.engines.alpha.guideline_matching.generic.journey.journey_next_step_selection import ( JourneyNextStepSelectionSchema, ) from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.engines.alpha.tool_calling.single_tool_batch import ( NonConsequentialToolBatchSchema, SingleToolBatchSchema, ) from parlant.core.loggers import Logger from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, ModelSize, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, BaseStreamingTextGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( CustomerModerationContext, BaseModerationService, ModerationCheck, ModerationService, ModerationTag, ) from parlant.core.tracer import Tracer RATE_LIMIT_ERROR_MESSAGE = ( "OpenAI API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your OpenAI account balance and billing status.\n" "- Review your API usage limits in OpenAI's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://platform.openai.com/docs/guides/rate-limits/usage-tiers\n" ) class OpenAIEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name if "5.1" in model_name: model_name_query = model_name.replace("5.1", "5") else: model_name_query = model_name self.encoding = tiktoken.encoding_for_model(model_name_query) @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class OpenAISchematicGenerator(BaseSchematicGenerator[T]): supported_openai_params = ["temperature", "logit_bias", "max_tokens"] supported_hints = supported_openai_params + ["strict"] unsupported_params_by_model: dict[str, list[str]] = { "gpt-5": ["temperature"], } def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, tokenizer_model_name: str | None = None, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient(api_key=os.environ["OPENAI_API_KEY"]) self._tokenizer = OpenAIEstimatingTokenizer( model_name=tokenizer_model_name or self.model_name ) @property @override def id(self) -> str: return f"openai/{self.model_name}" @property @override def tokenizer(self) -> OpenAIEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"OpenAI LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) def _list_arguments(self, hints: Mapping[str, Any]) -> Mapping[str, Any]: exclude_params = [ k for k in self.supported_openai_params for prefix, excluded in self.unsupported_params_by_model.items() if self.model_name.startswith(prefix) and k in excluded ] return { k: v for k, v in hints.items() if k in self.supported_openai_params and k not in exclude_params } async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() openai_api_arguments = self._list_arguments(hints) if hints.get("strict", False): t_start = time.time() try: response = await self._client.beta.chat.completions.parse( messages=[{"role": "developer", "content": prompt}], model=self.model_name, response_format=self.schema, **openai_api_arguments, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) parsed_object = response.choices[0].message.parsed assert parsed_object assert response.usage assert response.usage.prompt_tokens_details await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, cached_input_tokens=response.usage.prompt_tokens_details.cached_tokens or 0, ) return SchematicGenerationResult[T]( content=parsed_object, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={ "cached_input_tokens": response.usage.prompt_tokens_details.cached_tokens or 0 }, ), ), ) else: try: t_start = time.time() response = await self._client.chat.completions.create( messages=[{"role": "developer", "content": prompt}], model=self.model_name, response_format={"type": "json_object"}, **openai_api_arguments, ) t_end = time.time() except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.choices[0].message.content or "{}" try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) assert response.usage assert response.usage.prompt_tokens_details await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, cached_input_tokens=response.usage.prompt_tokens_details.cached_tokens or 0, ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={ "cached_input_tokens": response.usage.prompt_tokens_details.cached_tokens or 0 }, ), ), ) except ValidationError as e: self.logger.error( f"Error: {e.json(indent=2)}\nJSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class GPT_4o(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-4o-2024-11-20", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class GPT_4o_24_08_06(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-4o-2024-08-06", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class GPT_4_1(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gpt-4.1", logger=logger, tracer=tracer, meter=meter, tokenizer_model_name="gpt-4o-2024-11-20", ) @property @override def max_tokens(self) -> int: return 128 * 1024 class GPT_4o_Mini(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-4o-mini", logger=logger, tracer=tracer, meter=meter) self._token_estimator = OpenAIEstimatingTokenizer(model_name=self.model_name) @property @override def max_tokens(self) -> int: return 128 * 1024 class GPT_4_1_Mini(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-4.1-mini", logger=logger, tracer=tracer, meter=meter) self._token_estimator = OpenAIEstimatingTokenizer(model_name=self.model_name) @property @override def max_tokens(self) -> int: return 128 * 1024 class GPT_4_1_Nano(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-4.1-nano", logger=logger, tracer=tracer, meter=meter) self._token_estimator = OpenAIEstimatingTokenizer(model_name=self.model_name) @property @override def max_tokens(self) -> int: return 128 * 1024 class GPT_5_1(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-5.1", logger=logger, tracer=tracer, meter=meter) self._token_estimator = OpenAIEstimatingTokenizer(model_name=self.model_name) @property @override def max_tokens(self) -> int: return 400_000 class GPT_5_Mini(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-5-mini", logger=logger, tracer=tracer, meter=meter) self._token_estimator = OpenAIEstimatingTokenizer(model_name=self.model_name) @property @override def max_tokens(self) -> int: return 400_000 class GPT_5_Nano(OpenAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="gpt-5-nano", logger=logger, tracer=tracer, meter=meter) self._token_estimator = OpenAIEstimatingTokenizer(model_name=self.model_name) @property @override def max_tokens(self) -> int: return 400_000 # ============================================================================ # Streaming Text Generators # ============================================================================ # Pattern to detect word boundaries for chunking # Matches after any whitespace character _WORD_BOUNDARY_PATTERN = re.compile(r"(?<=\s)") # Number of words to buffer before yielding a chunk _WORDS_PER_CHUNK = 3 class OpenAIStreamingTextGenerator(BaseStreamingTextGenerator): """Streaming text generator using OpenAI's streaming API. Buffers tokens into word-sized chunks for smoother frontend rendering. """ supported_openai_params = ["temperature", "max_tokens"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, tokenizer_model_name: str | None = None, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient(api_key=os.environ["OPENAI_API_KEY"]) self._tokenizer = OpenAIEstimatingTokenizer( model_name=tokenizer_model_name or self.model_name ) @property @override def id(self) -> str: return f"openai-streaming/{self.model_name}" @property @override def tokenizer(self) -> OpenAIEstimatingTokenizer: return self._tokenizer def _list_arguments(self, hints: Mapping[str, Any]) -> Mapping[str, Any]: return {k: v for k, v in hints.items() if k in self.supported_openai_params} @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> tuple[AsyncIterator[str | None], Callable[[], UsageInfo]]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() openai_api_arguments = self._list_arguments(hints) try: stream = await self._client.chat.completions.create( messages=[{"role": "developer", "content": prompt}], model=self.model_name, stream=True, stream_options={"include_usage": True}, **openai_api_arguments, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise # Track usage from final chunk usage_info: UsageInfo | None = None async def chunk_generator() -> AsyncIterator[str | None]: nonlocal usage_info # Buffer for accumulating tokens into word-sized chunks buffer = "" async for chunk in stream: # Check for usage in final chunk (when stream_options include_usage is set) if chunk.usage is not None: self.logger.trace(chunk.usage.model_dump_json(indent=2)) cached_tokens = 0 if chunk.usage.prompt_tokens_details: cached_tokens = chunk.usage.prompt_tokens_details.cached_tokens or 0 usage_info = UsageInfo( input_tokens=chunk.usage.prompt_tokens, output_tokens=chunk.usage.completion_tokens, extra={"cached_input_tokens": cached_tokens}, ) if chunk.choices and chunk.choices[0].delta.content: token = chunk.choices[0].delta.content buffer += token # Count word boundaries in buffer boundaries = list(_WORD_BOUNDARY_PATTERN.finditer(buffer)) if len(boundaries) >= _WORDS_PER_CHUNK: # Yield up to the last complete word boundary last_boundary = boundaries[_WORDS_PER_CHUNK - 1] chunk_text = buffer[: last_boundary.end()] buffer = buffer[last_boundary.end() :] yield chunk_text # Yield any remaining content in the buffer if buffer: yield buffer # Record metrics if we have usage info if usage_info is not None: await record_llm_metrics( self.meter, self.model_name, schema_name="streaming", input_tokens=usage_info.input_tokens, output_tokens=usage_info.output_tokens, cached_input_tokens=usage_info.extra.get("cached_input_tokens", 0) if usage_info.extra else 0, ) # Signal completion yield None def get_usage() -> UsageInfo: if usage_info is None: # Fallback if usage wasn't available return UsageInfo(input_tokens=0, output_tokens=0) return usage_info return chunk_generator(), get_usage class GPT_4_1_Streaming(OpenAIStreamingTextGenerator): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="gpt-4.1", logger=logger, tracer=tracer, meter=meter, tokenizer_model_name="gpt-4o-2024-11-20", ) # ============================================================================ # Embedders # ============================================================================ class OpenAIEmbedder(BaseEmbedder): supported_arguments = ["dimensions"] def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(logger, tracer, meter, model_name) self._client = AsyncClient(api_key=os.environ["OPENAI_API_KEY"]) self._tokenizer = OpenAIEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"openai/{self.model_name}" @property @override def tokenizer(self) -> OpenAIEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: filtered_hints = {k: v for k, v in hints.items() if k in self.supported_arguments} try: response = await self._client.embeddings.create( model=self.model_name, input=texts, **filtered_hints, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise vectors = [data_point.embedding for data_point in response.data] return EmbeddingResult(vectors=vectors) class OpenAITextEmbedding3Large(OpenAIEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="text-embedding-3-large", logger=logger, tracer=tracer, meter=meter ) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 3072 class OpenAITextEmbedding3Small(OpenAIEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="text-embedding-3-small", logger=logger, tracer=tracer, meter=meter ) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 1536 class OpenAIModerationService(BaseModerationService): def __init__(self, model_name: str, logger: Logger, meter: Meter) -> None: super().__init__(logger, meter) self.model_name = model_name self._client = AsyncClient(api_key=os.environ["OPENAI_API_KEY"]) self._hist_moderation_request_duration = meter.create_duration_histogram( name="moderation", description="Duration of moderation requests in milliseconds", ) @override async def do_moderate(self, context: CustomerModerationContext) -> ModerationCheck: def extract_tags(category: str) -> list[ModerationTag]: mapping: dict[str, list[ModerationTag]] = { "sexual": ["sexual"], "sexual_minors": ["sexual", "illicit"], "harassment": ["harassment"], "harassment_threatening": ["harassment", "illicit"], "hate": ["hate"], "hate_threatening": ["hate", "illicit"], "illicit": ["illicit"], "illicit_violent": ["illicit", "violence"], "self_harm": ["self-harm"], "self_harm_intent": ["self-harm", "violence"], "self_harm_instructions": ["self-harm", "illicit"], "violence": ["violence"], "violence_graphic": ["violence", "harassment"], } return mapping.get(category.replace("/", "_").replace("-", "_"), []) response = await self._client.moderations.create( input=context.message, model=self.model_name, ) result = response.results[0] return ModerationCheck( flagged=result.flagged, tags=list( set( chain.from_iterable( extract_tags(category) for category, detected in result.categories if detected ) ) ), ) class OmniModeration(OpenAIModerationService): def __init__(self, logger: Logger, meter: Meter) -> None: super().__init__(model_name="omni-moderation-latest", logger=logger, meter=meter) class OpenAIService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("OPENAI_API_KEY"): return """\ You're using the OpenAI NLP service, but OPENAI_API_KEY is not set. Please set OPENAI_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self._logger = logger self._tracer = tracer self._meter = meter self._logger.info("Initialized OpenAIService") @property @override def supports_streaming(self) -> bool: return True @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: return GPT_4_1_Streaming(self._logger, self._tracer, self._meter) @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> OpenAISchematicGenerator[T]: match hints.get("model_size", ModelSize.AUTO): case ModelSize.AUTO: return { SingleToolBatchSchema: GPT_4o[SingleToolBatchSchema], NonConsequentialToolBatchSchema: GPT_4_1[NonConsequentialToolBatchSchema], JourneyBacktrackNodeSelectionSchema: GPT_4_1[ JourneyBacktrackNodeSelectionSchema ], CannedResponseDraftSchema: GPT_4_1[CannedResponseDraftSchema], CannedResponseSelectionSchema: GPT_4_1[CannedResponseSelectionSchema], JourneyNextStepSelectionSchema: GPT_4_1[JourneyNextStepSelectionSchema], JourneyBacktrackCheckSchema: GPT_4_1_Mini[JourneyBacktrackCheckSchema], }.get(t, GPT_4o_24_08_06[t])(self._logger, self._tracer, self._meter) # type: ignore case ModelSize.NANO: match hints.get("model_generation", "auto"): case "auto" | "stable": match hints.get("model_type", "auto"): case "auto" | "standard": return GPT_4_1_Nano[t](self._logger, self._tracer, self._meter) # type: ignore case "reasoning": return GPT_5_Nano[t](self._logger, self._tracer, self._meter) # type: ignore case "latest": match hints.get("model_type", "auto"): case "standard": return GPT_4_1_Nano[t](self._logger, self._tracer, self._meter) # type: ignore case "auto" | "reasoning": return GPT_5_Nano[t](self._logger, self._tracer, self._meter) # type: ignore case ModelSize.MINI: match hints.get("model_generation", "auto"): case "auto" | "stable": match hints.get("model_type", "auto"): case "auto" | "standard": return GPT_4_1_Mini[t](self._logger, self._tracer, self._meter) # type: ignore case "reasoning": return GPT_5_Mini[t](self._logger, self._tracer, self._meter) # type: ignore case "latest": match hints.get("model_type", "auto"): case "standard": return GPT_4_1_Mini[t](self._logger, self._tracer, self._meter) # type: ignore case "auto" | "reasoning": return GPT_5_Mini[t](self._logger, self._tracer, self._meter) # type: ignore case _: match hints.get("model_type", "auto"): case "reasoning": return GPT_5_1[t](self._logger, self._tracer, self._meter) # type: ignore case _: return GPT_4o_24_08_06[t](self._logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: match hints.get("model_size", ModelSize.AUTO): case ModelSize.AUTO | ModelSize.LARGE: return OpenAITextEmbedding3Large(self._logger, self._tracer, self._meter) case _: return OpenAITextEmbedding3Small(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> ModerationService: return OmniModeration(self._logger, self._meter) ================================================ FILE: src/parlant/adapters/nlp/openrouter_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import time from openai import ( APIConnectionError, APIResponseValidationError, APITimeoutError, AsyncClient, BadRequestError, ConflictError, InternalServerError, RateLimitError, ) from typing import Any, Callable, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( ModerationService, NoModeration, ) from parlant.core.tracer import Tracer RATE_LIMIT_ERROR_MESSAGE = """\ OpenRouter API rate limit exceeded. Possible reasons: 1. Your account may have insufficient API credits. 2. You may be using a free-tier account with limited request capacity. 3. You might have exceeded the requests-per-minute limit for your account. Recommended actions: - Check your OpenRouter account balance and billing status. - Review your API usage limits in OpenRouter's dashboard. - For more details on rate limits and usage tiers, visit: https://openrouter.ai/docs/api-reference/limits """ class OpenRouterEmptyEmbeddingResponseError(Exception): """Raised when OpenRouter returns an embedding response with no vectors.""" class OpenRouterEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name # Use gpt-4 encoding as default for token estimation self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class OpenRouterSchematicGenerator(BaseSchematicGenerator[T]): supported_openrouter_params = ["temperature", "max_tokens"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._logger = logger # Build extra headers from environment variables extra_headers = {} if "OPENROUTER_HTTP_REFERER" in os.environ: extra_headers["HTTP-Referer"] = os.environ["OPENROUTER_HTTP_REFERER"] if "OPENROUTER_SITE_NAME" in os.environ: extra_headers["X-Title"] = os.environ["OPENROUTER_SITE_NAME"] self._client = AsyncClient( base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"], default_headers=extra_headers if extra_headers else None, ) self._tokenizer = OpenRouterEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"openrouter/{self.model_name}" @property @override def tokenizer(self) -> OpenRouterEstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: # Default implementation - should be overridden by subclasses return 8192 @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, OpenRouterEmptyEmbeddingResponseError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() openrouter_api_arguments = { k: v for k, v in hints.items() if k in self.supported_openrouter_params } t_start = time.time() # Try with JSON mode first, but catch errors gracefully response = None try: # Try with JSON mode response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, response_format={"type": "json_object"}, **openrouter_api_arguments, ) except BadRequestError as e: # Check if it's a JSON mode error error_str = str(e) if "JSON mode" in error_str or "json_object" in error_str.lower(): self._logger.error( f"\nModel '{self.model_name}' does not support JSON mode.\n" f"Please switch to a model that supports JSON mode (e.g., 'openai/gpt-4o', 'anthropic/claude-3.5-sonnet').\n" f"Attempting to continue without JSON mode enforcement, but results may be less reliable.\n" ) # Retry without JSON mode with a system message to instruct JSON output try: # Add system message to instruct the model to output JSON json_instruction = "IMPORTANT: You must respond with ONLY valid JSON. No explanatory text before or after the JSON. The response must be a valid JSON object." response = await self._client.chat.completions.create( messages=[ {"role": "system", "content": json_instruction}, {"role": "user", "content": prompt}, ], model=self.model_name, **openrouter_api_arguments, ) except Exception as retry_error: self._logger.error( f"\nFailed to use model '{self.model_name}' even without JSON mode.\n" f"Error: {retry_error}\n" f"Please change your model to one that supports JSON mode or use a different model entirely.\n" ) raise else: # Some other BadRequest error - just log it once and raise self._logger.error(f"OpenRouter API BadRequest: {e}") raise except RateLimitError: self._logger.error( f"\nRate limit exceeded for model '{self.model_name}'.\n" f"{RATE_LIMIT_ERROR_MESSAGE}\n" f"Consider:\n" f" - Using a different model\n" f" - Waiting a moment before retrying\n" f" - Adding your own API key for higher limits\n" ) raise except Exception as e: self._logger.error( f"\nOpenRouter API error with model '{self.model_name}': {type(e).__name__}\n" f"{e}\n" f"Consider switching to a more compatible model.\n" ) raise t_end = time.time() if response.usage: self._logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.choices[0].message.content or "{}" # Check if we got empty response if not raw_content.strip() or raw_content.strip() == "{}": self._logger.error( f"\nModel '{self.model_name}' returned empty or invalid JSON.\n" f"Response: {raw_content}\n" f"This model may not be compatible with structured output requirements.\n" f"Please switch to a model that supports JSON mode (e.g., 'openai/gpt-4o', 'anthropic/claude-3.5-sonnet').\n" ) # Set empty JSON as fallback json_content = {} else: try: json_content = json.loads(normalize_json_output(raw_content)) # Check if parsed JSON is empty if not json_content or json_content == {}: self._logger.warning( "Model returned empty JSON object. Attempting to find JSON in response..." ) # Try to find JSON in the response try: json_content = jsonfinder.only_json(raw_content)[2] if json_content and json_content != {}: self._logger.info("Found valid JSON content within response.") except Exception: self._logger.error( f"Could not extract valid JSON from response: {raw_content}" ) except json.JSONDecodeError: self._logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content}") try: # Try to extract JSON using jsonfinder json_content = jsonfinder.only_json(raw_content)[2] self._logger.warning("Found JSON content within model response; continuing...") except Exception as finder_error: self._logger.error( f"\nCould not parse JSON from model response.\n" f"Raw response: {raw_content}\n" f"Error: {finder_error}\n" f"Model '{self.model_name}' may not be compatible.\n" f"Consider switching to a model that supports structured output.\n" ) json_content = {} try: content = self.schema.model_validate(json_content) assert response.usage return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={ "cached_input_tokens": getattr( response.usage, "prompt_cache_hit_tokens", 0, ) }, ), ), ) except ValidationError as e: self._logger.error( f"\nJSON content returned by '{self.model_name}' does not match expected schema.\n" f"Schema: {self.schema.__name__}\n" f"Raw response: {raw_content}\n" f"Parsed JSON: {json.dumps(json_content, indent=2) if json_content else 'Empty'}\n" f"Validation errors: {str(e)}\n" f"This model may not be producing valid structured output.\n" f"Consider switching to a model that supports JSON mode.\n" ) raise class OpenRouterGPT4O(OpenRouterSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="openai/gpt-4o", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class OpenRouterGPT4OMini(OpenRouterSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="openai/gpt-4o-mini", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class OpenRouterClaude35Sonnet(OpenRouterSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="anthropic/claude-3.5-sonnet", logger=logger, tracer=tracer, meter=meter ) @property @override def max_tokens(self) -> int: return 8192 class OpenRouterLlama33_70B(OpenRouterSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="meta-llama/llama-3.3-70b-instruct", logger=logger, tracer=tracer, meter=meter, ) @property @override def max_tokens(self) -> int: return 8192 class OpenRouterEmbedder(BaseEmbedder): supported_arguments = ["dimensions"] # Known embedding model dimensions _KNOWN_DIMENSIONS: dict[str, int] = { "openai/text-embedding-3-large": 3072, "openai/text-embedding-3-small": 1536, "openai/text-embedding-ada-002": 1536, "qwen/qwen3-embedding-8b": 4096, "qwen/qwen-embedding-v2": 1536, } def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(logger, tracer, meter, model_name) # Build extra headers from environment variables extra_headers = {} if "OPENROUTER_HTTP_REFERER" in os.environ: extra_headers["HTTP-Referer"] = os.environ["OPENROUTER_HTTP_REFERER"] if "OPENROUTER_SITE_NAME" in os.environ: extra_headers["X-Title"] = os.environ["OPENROUTER_SITE_NAME"] self._client = AsyncClient( base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"], default_headers=extra_headers if extra_headers else None, ) self._tokenizer = OpenRouterEstimatingTokenizer(model_name=self.model_name) # Cache dimensions after first API call if not known self._cached_dimensions: int | None = None @property @override def id(self) -> str: return f"openrouter/{self.model_name}" @property @override def tokenizer(self) -> OpenRouterEstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: # Default max tokens for embedding models return 8192 @property @override def dimensions(self) -> int: # Check environment variable override first if "OPENROUTER_EMBEDDER_DIMENSIONS" in os.environ: return int(os.environ["OPENROUTER_EMBEDDER_DIMENSIONS"]) # Return cached dimensions if available if self._cached_dimensions is not None: return self._cached_dimensions # Check known dimensions lookup for model_key, dims in self._KNOWN_DIMENSIONS.items(): if model_key in self.model_name: return dims # Default fallback - most embedding models use 1536 or 3072 # This will be updated after first API call return 1536 @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, OpenRouterEmptyEmbeddingResponseError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: filtered_hints = {k: v for k, v in hints.items() if k in self.supported_arguments} try: response = await self._client.embeddings.create( model=self.model_name, input=texts, **filtered_hints, ) except ValueError as exc: if "No embedding data received" in str(exc): raise OpenRouterEmptyEmbeddingResponseError(str(exc)) from exc raise except RateLimitError: self.logger.error( f"\nRate limit exceeded for embedder model '{self.model_name}'.\n" f"{RATE_LIMIT_ERROR_MESSAGE}\n" f"Consider:\n" f" - Using a different embedder model\n" f" - Waiting a moment before retrying\n" f" - Adding your own API key for higher limits\n" ) raise if not response.data: raise OpenRouterEmptyEmbeddingResponseError("No embedding data received") vectors = [data_point.embedding for data_point in response.data] # Cache dimensions from first response if not already cached and not in known list if self._cached_dimensions is None and vectors: actual_dims = len(vectors[0]) # Only cache if different from default or if not found in known dimensions if actual_dims != 1536 or not any( key in self.model_name for key in self._KNOWN_DIMENSIONS ): self._cached_dimensions = actual_dims self.logger.debug( f"Detected embedding dimensions for '{self.model_name}': {actual_dims}" ) return EmbeddingResult(vectors=vectors) class OpenRouterTextEmbedding3Large(OpenRouterEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="openai/text-embedding-3-large", logger=logger, tracer=tracer, meter=meter ) @property @override def max_tokens(self) -> int: return 8192 @property @override def dimensions(self) -> int: return 3072 class OpenRouterService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("OPENROUTER_API_KEY"): return """\ You're using the OpenRouter NLP service, but OPENROUTER_API_KEY is not set. Please set OPENROUTER_API_KEY in your environment before running Parlant. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self._logger = logger self._tracer = tracer self._meter = meter self._logger.info("Initialized OpenRouterService") # Get model_name from environment variable self.model_name = os.environ.get("OPENROUTER_MODEL", "openai/gpt-4o") # Get embedder_model_name from environment variable self.embedder_model_name = os.environ.get( "OPENROUTER_EMBEDDER_MODEL", "openai/text-embedding-3-large" ) self._logger.info(f"OpenRouter model name: {self.model_name}") self._logger.info(f"OpenRouter embedder model name: {self.embedder_model_name}") # Create dynamic embedder class that can be resolved from the container # This captures embedder_model_name in a closure so the container can resolve it embedder_model = self.embedder_model_name class DynamicOpenRouterEmbedder(OpenRouterEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter): super().__init__( model_name=embedder_model, logger=logger, tracer=tracer, meter=meter ) self._dynamic_embedder_class = DynamicOpenRouterEmbedder @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") def _get_specialized_generator_class( self, model_name: str, t: type[T], ) -> Callable[[Logger, Tracer, Meter], OpenRouterSchematicGenerator[T]]: """ Returns the specialized generator class for known models. For unknown models, creates a dynamic generator that works with any OpenRouter model. """ model_mapping: dict[ str, Callable[[Logger, Tracer, Meter], OpenRouterSchematicGenerator[T]] ] = { "openai/gpt-4o": lambda logger, tracer, meter: OpenRouterGPT4O[t]( # type: ignore logger, tracer, meter ), "openai/gpt-4o-mini": lambda logger, tracer, meter: OpenRouterGPT4OMini[t]( # type: ignore logger, tracer, meter ), "anthropic/claude-3.5-sonnet": lambda logger, tracer, meter: OpenRouterClaude35Sonnet[ t # type: ignore ](logger, tracer, meter), "meta-llama/llama-3.3-70b-instruct": lambda logger, tracer, meter: ( OpenRouterLlama33_70B[t]( # type: ignore logger, tracer, meter ) ), } # Check if we have a predefined generator for this model if generator_factory := model_mapping.get(model_name): return generator_factory # Create a dynamic generator for any OpenRouter model # Get max_tokens from environment variable or use sensible defaults based on model name max_tokens_str = os.environ.get("OPENROUTER_MAX_TOKENS") if max_tokens_str: max_tokens = int(max_tokens_str) else: # Provide sensible defaults based on model family if "gpt-4" in model_name: max_tokens = 128 * 1024 elif "claude" in model_name: max_tokens = 8192 elif "llama" in model_name or "gemma" in model_name: max_tokens = 8192 else: max_tokens = 8192 # Safe default for unknown models # Create dynamic generator class with the specific max_tokens final_max_tokens = max_tokens class DynamicOpenRouterGenerator(OpenRouterSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter): super().__init__(model_name=model_name, logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return final_max_tokens # Return a factory function that creates the properly typed instance def create_generator( logger: Logger, tracer: Tracer, meter: Meter ) -> OpenRouterSchematicGenerator[T]: return DynamicOpenRouterGenerator[t](logger, tracer, meter) # type: ignore return create_generator @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> OpenRouterSchematicGenerator[T]: generator_factory = self._get_specialized_generator_class(self.model_name, t) return generator_factory(self._logger, self._tracer, self._meter) @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: # Use OpenRouter embedder with the configured embedder model name # Default to text-embedding-3-large if not specified if self.embedder_model_name == "openai/text-embedding-3-large": return OpenRouterTextEmbedding3Large( logger=self._logger, tracer=self._tracer, meter=self._meter ) else: # Return instance of dynamic embedder class that can be resolved from container return self._dynamic_embedder_class( logger=self._logger, tracer=self._tracer, meter=self._meter ) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/qwen_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Maintainer: Ji Qing from __future__ import annotations import time from openai import ( APIConnectionError, APIResponseValidationError, APITimeoutError, AsyncClient, ConflictError, InternalServerError, RateLimitError, ) from typing import Any, Callable, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( ModerationService, NoModeration, ) from parlant.core.tracer import Tracer RATE_LIMIT_ERROR_MESSAGE = """\ Qwen API rate limit exceeded. Possible reasons: 1. Your account may have insufficient API credits. 2. You may be using a free-tier account with limited request capacity. 3. You might have exceeded the requests-per-minute limit for your account. Recommended actions: - Check your Qwen account balance and billing status. - Review your API usage limits in Qwen's dashboard. - For more details on rate limits and usage tiers, visit: https://help.aliyun.com/zh/model-studio/ """ QWEN_REGION_BASE_URLS = { "international": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", "domestic": "https://dashscope.aliyuncs.com/compatible-mode/v1", } def get_qwen_base_url() -> str: """Get the base URL for Qwen API based on region configuration. Priority: 1. QWEN_BASE_URL environment variable (explicit override) 2. QWEN_REGION environment variable (international/domestic) 3. Default to international region """ if base_url := os.environ.get("QWEN_BASE_URL"): return base_url region = os.environ.get("QWEN_REGION", "international").lower() if region not in QWEN_REGION_BASE_URLS: raise ValueError(f"Invalid QWEN_REGION '{region}'. Must be 'international' or 'domestic'.") return QWEN_REGION_BASE_URLS[region] class QwenEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: str) -> None: self.model_name = model_name self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) class QwenEmbedder(BaseEmbedder): supported_arguments = ["dimensions"] def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient( base_url=get_qwen_base_url(), api_key=os.environ.get("DASHSCOPE_API_KEY", ""), ) self._tokenizer = QwenEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"qwen/{self.model_name}" @property @override def tokenizer(self) -> QwenEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: filtered_hints = {k: v for k, v in hints.items() if k in self.supported_arguments} try: response = await self._client.embeddings.create( model=self.model_name, input=texts, **filtered_hints, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise vectors = [data_point.embedding for data_point in response.data] return EmbeddingResult(vectors=vectors) class QwenTextEmbedding_V4(QwenEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="text-embedding-v4", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 8192 @property def dimensions(self) -> int: return 1024 class QwenSchematicGenerator(BaseSchematicGenerator[T]): supported_qwen_params = ["temperature", "max_tokens"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncClient( base_url=get_qwen_base_url(), api_key=os.environ["DASHSCOPE_API_KEY"], ) self._tokenizer = QwenEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: return f"Qwen/{self.model_name}" @property @override def tokenizer(self) -> QwenEstimatingTokenizer: return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, ConflictError, RateLimitError, APIResponseValidationError, ), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Qwen LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() qwen_api_arguments = {k: v for k, v in hints.items() if k in self.supported_qwen_params} t_start = time.time() response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, max_tokens=8 * 1024, response_format={"type": "json_object"}, **qwen_api_arguments, ) t_end = time.time() if response.usage: self.logger.trace(response.usage.model_dump_json(indent=2)) raw_content = response.choices[0].message.content or "{}" try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") try: content = self.schema.model_validate(json_content) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, cached_input_tokens=getattr( response, "usage.prompt_cache_hit_tokens", 0, ), ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={ "cached_input_tokens": getattr( response, "usage.prompt_cache_hit_tokens", 0, ) }, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class Qwen_MAX(QwenSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="qwen-max", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 32 * 1024 class Qwen_Plus(QwenSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="qwen-plus", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: return 128 * 1024 class Qwen_2_5_72b(QwenSchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="qwen2.5-72b-instruct", logger=logger, tracer=tracer, meter=meter ) @property @override def max_tokens(self) -> int: return 128 * 1024 class QwenService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" if not os.environ.get("DASHSCOPE_API_KEY"): return """\ You're using the Qwen NLP service, but DASHSCOPE_API_KEY is not set. Please set DASHSCOPE_API_KEY in your environment before running Parlant. """ if region := os.environ.get("QWEN_REGION"): if region.lower() not in QWEN_REGION_BASE_URLS: return f"""\ Invalid QWEN_REGION '{region}'. Must be one of: {", ".join(QWEN_REGION_BASE_URLS.keys())} """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.logger = logger self._tracer = tracer self._meter = meter self.model_name = os.environ.get("QWEN_MODEL", "qwen-plus") self.logger.info(f"Initialized QwenService with model: {self.model_name}") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") def _get_specialized_generator_class( self, model_name: str, t: type[T], ) -> Callable[..., QwenSchematicGenerator[T]] | None: """ Returns the specialized generator class for known models """ model_mapping: dict[str, type[QwenSchematicGenerator[T]]] = { "qwen-max": Qwen_MAX[t], # type: ignore "qwen-plus": Qwen_Plus[t], # type: ignore "qwen2.5-72b-instruct": Qwen_2_5_72b[t], # type: ignore } if generator_class := model_mapping.get(model_name): return generator_class else: return None @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> QwenSchematicGenerator[T]: qwen_generator = self._get_specialized_generator_class(self.model_name, t) assert qwen_generator is not None, f"Unsupported Qwen model: {self.model_name}" return qwen_generator(self.logger, self._tracer, self._meter) @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return QwenTextEmbedding_V4(logger=self.logger, tracer=self._tracer, meter=self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/snowflake_cortex_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Maintainer: Tao Tang from __future__ import annotations import os import time import json from typing import Any, Mapping, Optional, Type, cast import httpx import tiktoken from typing_extensions import override from pydantic import ValidationError from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.loggers import Logger from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.tracer import Tracer HTTPX_TIMEOUT = httpx.Timeout(timeout=60.0, connect=5.0, read=60.0, write=60.0) class CortexEstimatingTokenizer(EstimatingTokenizer): def __init__(self, model_name: Optional[str] = None) -> None: self.model_name = model_name or "cl100k_base" try: self.encoding = tiktoken.encoding_for_model(self.model_name) except Exception: self.encoding = tiktoken.get_encoding("cl100k_base") @override async def estimate_token_count(self, prompt: str) -> int: return int(len(self.encoding.encode(prompt)) * 1.05) class CortexSchematicGenerator(BaseSchematicGenerator[T]): """ Snowflake Cortex chat generator via REST: POST {BASE}/api/v2/cortex/inference:complete """ _provider_params = ["temperature", "top_p", "top_k", "max_tokens", "stop"] supported_hints = _provider_params + ["strict"] def __init__(self, *, schema: type[T], logger: Logger, tracer: Tracer, meter: Meter) -> None: self.schema = schema self._base_url = os.environ["SNOWFLAKE_CORTEX_BASE_URL"].rstrip("/") self._token = os.environ["SNOWFLAKE_AUTH_TOKEN"] model_name = os.environ["SNOWFLAKE_CORTEX_CHAT_MODEL"] super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._tokenizer = CortexEstimatingTokenizer(self.model_name) self._client = httpx.AsyncClient(timeout=HTTPX_TIMEOUT) self._max_tokens_hint = int(os.environ.get("SNOWFLAKE_CORTEX_MAX_TOKENS", "8192")) @property @override def id(self) -> str: return f"snowflake-cortex/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: return self._max_tokens_hint def _headers(self) -> dict[str, str]: return { "Authorization": f"Bearer {self._token}", "Accept": "application/json", "Content-Type": "application/json", } @policy( [ retry( exceptions=( httpx.ReadTimeout, httpx.ConnectTimeout, httpx.RemoteProtocolError, ), max_exceptions=3, wait_times=(1.0, 2.0, 4.0), ), retry(httpx.HTTPStatusError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Cortex LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() schema: Type[T] = self.schema messages = [{"role": "user", "content": prompt}] payload: dict[str, Any] = { "model": self.model_name, "messages": messages, "stream": False, } for k in self._provider_params: if k in hints: payload[k] = hints[k] # Strict path: provider-enforced JSON schema if hints.get("strict", False): try: payload["response_format"] = { "type": "json", "schema": schema.model_json_schema(), } except Exception as e: # If schema export fails, fall back to local validation self.logger.debug(f"Strict schema export failed, falling back: {e}") url = f"{self._base_url}/api/v2/cortex/inference:complete" t0 = time.time() resp = await self._client.post(url, headers=self._headers(), json=payload) try: resp.raise_for_status() except httpx.HTTPStatusError as e: self.logger.error(f"Cortex COMPLETE error {e.response.status_code}: {e.response.text}") raise t1 = time.time() data = resp.json() msg = (data.get("choices") or [{}])[0].get("message", {}) raw = msg.get("content") if raw is None: cl = msg.get("content_list") or [] if cl and isinstance(cl[0], dict): raw = cl[0].get("text") if raw is None: raw = msg if msg else data # Parse JSON try: if isinstance(raw, str): normalized = normalize_json_output(raw) parsed = cast(dict[str, Any], json.loads(normalized)) elif isinstance(raw, dict): parsed = raw else: parsed = json.loads(str(raw)) except Exception: try: normalized = normalize_json_output(str(raw)) parsed = cast(dict[str, Any], json.loads(normalized)) except Exception as ex: self.logger.error(f"Failed to parse structured output: {ex}\nRaw: {raw}") raise # Validate against the schema model try: content = schema.model_validate(parsed) except ValidationError as ve: self.logger.error( f"Structured output validation failed:\n{ve.json(indent=2)}\nRaw: {raw}" ) raise usage_block = data.get("usage") or {} await record_llm_metrics( self.meter, self.model_name, schema_name=schema.__name__, input_tokens=usage_block.get("prompt_tokens", 0), output_tokens=usage_block.get("completion_tokens", 0), ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=schema.__name__, model=self.id, duration=(t1 - t0), usage=UsageInfo( input_tokens=usage_block.get("prompt_tokens", 0), output_tokens=usage_block.get("completion_tokens", 0), extra={}, ), ), ) class CortexEmbedder(BaseEmbedder): """Embeddings via Snowflake Cortex. Endpoint: POST {BASE}/api/v2/cortex/inference:embed """ supported_arguments = ["dimensions"] def __init__(self, *, logger: Logger, tracer: Tracer, meter: Meter) -> None: model_name = os.environ["SNOWFLAKE_CORTEX_EMBED_MODEL"] super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._base_url = os.environ["SNOWFLAKE_CORTEX_BASE_URL"].rstrip("/") self._token = os.environ["SNOWFLAKE_AUTH_TOKEN"] self._client = httpx.AsyncClient(timeout=HTTPX_TIMEOUT) self._tokenizer = CortexEstimatingTokenizer(self.model_name) self._dims = self._infer_dims(self.model_name) @property @override def id(self) -> str: return f"snowflake-cortex/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @property @override def dimensions(self) -> int: return self._dims @staticmethod def _infer_dims(model_name: str) -> int: n = model_name.lower() if "e5-base" in n: return 768 if "snowflake-arctic-embed-m" in n: return 768 if "snowflake-arctic-embed-l" in n: return 1024 return 768 def _headers(self) -> dict[str, str]: return { "Authorization": f"Bearer {self._token}", "Accept": "application/json", "Content-Type": "application/json", } @policy( [ retry( exceptions=( httpx.ReadTimeout, httpx.ConnectTimeout, httpx.RemoteProtocolError, ), max_exceptions=3, wait_times=(1.0, 2.0, 4.0), ), retry(httpx.HTTPStatusError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: payload: dict[str, Any] = {"model": self.model_name, "text": texts} if "dimensions" in hints: payload["dimensions"] = hints["dimensions"] url = f"{self._base_url}/api/v2/cortex/inference:embed" resp = await self._client.post(url, headers=self._headers(), json=payload) try: resp.raise_for_status() except httpx.HTTPStatusError as e: self.logger.error(f"Cortex EMBED error {e.response.status_code}: {e.response.text}") raise data = resp.json() vectors: list[list[float]] = [] for row in data.get("data", []): emb = row.get("embedding") if isinstance(emb, list) and emb and isinstance(emb[0], list): emb = emb[0] vectors.append(emb) return EmbeddingResult(vectors=vectors) @property @override def max_tokens(self) -> int: return 8192 class SnowflakeCortexService(NLPService): """Parlant adapter for Snowflake Cortex (chat + embeddings). Environment Variables: SNOWFLAKE_CORTEX_BASE_URL: Base account URL (e.g. https://.snowflakecomputing.com) SNOWFLAKE_AUTH_TOKEN: OAuth/Keypair JWT/PAT token SNOWFLAKE_CORTEX_CHAT_MODEL: Chat model name SNOWFLAKE_CORTEX_EMBED_MODEL: Embedding model name SNOWFLAKE_CORTEX_MAX_TOKENS: Optional max token hint """ @staticmethod def verify_environment() -> str | None: missing = [] if not os.environ.get("SNOWFLAKE_CORTEX_BASE_URL"): missing.append( "SNOWFLAKE_CORTEX_BASE_URL (e.g. https://.snowflakecomputing.com)" ) if not os.environ.get("SNOWFLAKE_AUTH_TOKEN"): missing.append("SNOWFLAKE_AUTH_TOKEN (OAuth/Keypair JWT/PAT)") if not os.environ.get("SNOWFLAKE_CORTEX_CHAT_MODEL"): missing.append("SNOWFLAKE_CORTEX_CHAT_MODEL") if not os.environ.get("SNOWFLAKE_CORTEX_EMBED_MODEL"): missing.append("SNOWFLAKE_CORTEX_EMBED_MODEL") if missing: return "Missing Snowflake Cortex settings:\n - " + "\n - ".join(missing) return None def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: self.logger = logger self._tracer = tracer self._meter = meter self._base_url = os.environ["SNOWFLAKE_CORTEX_BASE_URL"].rstrip("/") self._token = os.environ["SNOWFLAKE_AUTH_TOKEN"] self._chat_model = os.environ["SNOWFLAKE_CORTEX_CHAT_MODEL"] self._embed_model = os.environ["SNOWFLAKE_CORTEX_EMBED_MODEL"] self.logger.info( f"SnowflakeCortexService: chat={self._chat_model} | embed={self._embed_model} @ {self._base_url}" ) @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> SchematicGenerator[T]: return CortexSchematicGenerator[t]( # type: ignore[valid-type,misc] schema=t, logger=self.logger, tracer=self._tracer, meter=self._meter ) @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: return CortexEmbedder(logger=self.logger, tracer=self._tracer, meter=self._meter) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/together_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time from pydantic import ValidationError from together import AsyncTogether # type: ignore from together.error import ( # type: ignore RateLimitError, Timeout, APIConnectionError, APIError, ServiceUnavailableError, ) from typing import Any, Callable, Mapping from typing_extensions import override import jsonfinder # type: ignore import os import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.adapters.nlp.hugging_face import HuggingFaceEstimatingTokenizer from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.tokenization import EstimatingTokenizer RATE_LIMIT_ERROR_MESSAGE = ( "Together API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Together account balance and billing status.\n" "- Review your API usage limits in Together's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://docs.together.ai/docs/rate-limits" ) class LlamaEstimatingTokenizer(EstimatingTokenizer): def __init__(self) -> None: self.encoding = tiktoken.encoding_for_model("gpt-4o-2024-08-06") @override async def estimate_token_count(self, prompt: str) -> int: tokens = self.encoding.encode(prompt) return len(tokens) + 36 class TogetherAISchematicGenerator(BaseSchematicGenerator[T]): supported_hints = ["temperature", "max_tokens", "top_p", "top_k"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncTogether(api_key=os.environ.get("TOGETHER_API_KEY")) self._estimating_tokenizer = LlamaEstimatingTokenizer() @property @override def id(self) -> str: return self.model_name @property @override def tokenizer(self) -> LlamaEstimatingTokenizer: return self._estimating_tokenizer @property @override def max_tokens(self) -> int: # Default max tokens, can be overridden by specific model classes return 128 * 1024 @policy( [ retry( exceptions=( RateLimitError, Timeout, APIConnectionError, APIError, ) ), retry(ServiceUnavailableError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Together LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() together_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} t_start = time.time() try: response = await self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, response_format={"type": "json_object"}, **together_api_arguments, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise t_end = time.time() raw_content = response.choices[0].message.content or "{}" try: json_content = normalize_json_output(raw_content) json_object = jsonfinder.only_json(json_content)[2] except Exception: self.logger.error( f"Failed to extract JSON returned by {self.model_name}:\n{raw_content}" ) raise try: model_content = self.schema.model_validate(json_object) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, extra={}, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class Llama3_1_8B(TogetherAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", logger=logger, tracer=tracer, meter=meter, ) class Llama3_1_70B(TogetherAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", logger=logger, tracer=tracer, meter=meter, ) class Llama3_1_405B(TogetherAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", logger=logger, tracer=tracer, meter=meter, ) class Llama3_3_70B(TogetherAISchematicGenerator[T]): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="meta-llama/Llama-3.3-70B-Instruct-Turbo", logger=logger, tracer=tracer, meter=meter, ) class TogetherAIEmbedder(BaseEmbedder): def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = AsyncTogether(api_key=os.environ.get("TOGETHER_API_KEY")) @policy( [ retry( exceptions=( RateLimitError, Timeout, APIConnectionError, APIError, ) ), retry(ServiceUnavailableError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: _ = hints try: response = await self._client.embeddings.create( model=self.model_name, input=texts, ) except RateLimitError: self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise vectors = [data_point.embedding for data_point in response.data] return EmbeddingResult(vectors=vectors) class M2Bert32K(TogetherAIEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name="togethercomputer/m2-bert-80M-32k-retrieval", logger=logger, tracer=tracer, meter=meter, ) self._estimating_tokenizer = HuggingFaceEstimatingTokenizer(self.model_name) @property @override def id(self) -> str: return self.model_name @property @override def max_tokens(self) -> int: return 32 * 1024 @property @override def tokenizer(self) -> HuggingFaceEstimatingTokenizer: return self._estimating_tokenizer @property @override def dimensions(self) -> int: return 768 class CustomTogetherAISchematicGenerator(TogetherAISchematicGenerator[T]): """Generic Together AI generator that accepts any model name.""" def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__( model_name=model_name, logger=logger, tracer=tracer, meter=meter, ) class CustomTogetherAIEmbedder(TogetherAIEmbedder): """Generic Together AI embedder that accepts any model name.""" def __init__(self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name=model_name, logger=logger, tracer=tracer, meter=meter) self._estimating_tokenizer = HuggingFaceEstimatingTokenizer(model_name) self._dimensions = int(os.environ.get("TOGETHER_EMBEDDING_DIMENSIONS", "768")) @property @override def id(self) -> str: return self.model_name @property @override def max_tokens(self) -> int: return int(os.environ.get("TOGETHER_EMBEDDING_MAX_TOKENS", "32768")) @property @override def tokenizer(self) -> HuggingFaceEstimatingTokenizer: return self._estimating_tokenizer @property @override def dimensions(self) -> int: return self._dimensions class TogetherService(NLPService): @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" required_vars = { "TOGETHER_API_KEY": "your-together-api-key", "TOGETHER_MODEL": "meta-llama/Llama-3.3-70B-Instruct-Turbo", "TOGETHER_EMBEDDING_MODEL": "togethercomputer/m2-bert-80M-32k-retrieval", } missing_vars = [] for var_name, example_value in required_vars.items(): if not os.environ.get(var_name): missing_vars.append(f'export {var_name}="{example_value}"') if missing_vars: return f"""\ You're using the Together AI NLP service, but the following environment variables are not set: {chr(10).join(missing_vars)} Please set these environment variables before running Parlant. Available models can be found at: https://docs.together.ai/docs/inference-models """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.model_name = os.environ.get( "TOGETHER_MODEL", "meta-llama/Llama-3.3-70B-Instruct-Turbo" ) self.embedding_model = os.environ.get( "TOGETHER_EMBEDDING_MODEL", "togethercomputer/m2-bert-80M-32k-retrieval" ) self._logger = logger self._tracer = tracer self._meter = meter self._logger.info(f"Initialized TogetherService with model: {self.model_name}") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") def _get_specialized_generator_class( self, model_name: str, schema_type: type[T], ) -> Callable[[Logger], TogetherAISchematicGenerator[T]] | None: """ Returns the specialized generator class for known models, or None for custom models. """ model_to_class: dict[str, Callable[[Logger], TogetherAISchematicGenerator[T]]] = { "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": Llama3_1_8B[schema_type], # type: ignore "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo": Llama3_1_70B[schema_type], # type: ignore "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo": Llama3_1_405B[schema_type], # type: ignore "meta-llama/Llama-3.3-70B-Instruct-Turbo": Llama3_3_70B[schema_type], # type: ignore } return model_to_class.get(model_name) @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> TogetherAISchematicGenerator[T]: specialized_class = self._get_specialized_generator_class(self.model_name, schema_type=t) if specialized_class: self._logger.debug(f"Using specialized generator for model: {self.model_name}") return specialized_class(self._logger) else: self._logger.debug(f"Using custom generator for model: {self.model_name}") return CustomTogetherAISchematicGenerator[t]( # type: ignore model_name=self.model_name, logger=self._logger, tracer=self._tracer, meter=self._meter, ) def _get_specialized_embedder_class( self, model_name: str, ) -> Callable[[Logger, Tracer, Meter], TogetherAIEmbedder] | None: """ Returns the specialized embedder class for known models, or None for custom models. """ model_to_class: dict[str, Callable[[Logger, Tracer, Meter], TogetherAIEmbedder]] = { "togethercomputer/m2-bert-80M-32k-retrieval": M2Bert32K, } return model_to_class.get(model_name) @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: specialized_class = self._get_specialized_embedder_class(self.embedding_model) if specialized_class: self._logger.debug(f"Using specialized embedder for model: {self.embedding_model}") return specialized_class(self._logger, self._tracer, self._meter) else: self._logger.debug(f"Using custom embedder for model: {self.embedding_model}") return CustomTogetherAIEmbedder( model_name=self.embedding_model, logger=self._logger, tracer=self._tracer, meter=self._meter, ) @override async def get_moderation_service(self) -> ModerationService: return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/vertex_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Maintainer: Agam Dubey hello.world.agam@gmail.com # Moderation service needs to be added # Usage guidelines - Use gemini-2.5-pro and claude sonnet 4 models for best results # Set env variables: VERTEX_AI_PROJECT_ID VERTEX_AI_REGION, VERTEX_AI_MODEL import os import time from typing import Any, Mapping, cast from typing_extensions import override from enum import Enum import google.auth import google.api_core.exceptions import google.genai # type: ignore import google.genai.types # type: ignore from google.api_core.exceptions import NotFound, TooManyRequests, ResourceExhausted, ServerError from anthropic import ( AsyncAnthropicVertex, APIConnectionError, APIResponseValidationError, APITimeoutError, InternalServerError, RateLimitError, ) # type: ignore import jsonfinder # type: ignore from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.moderation import ModerationService, NoModeration from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerator, FallbackSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.loggers import Logger class ModelProvider(Enum): """Enum to identify the model provider.""" ANTHROPIC = "anthropic" GOOGLE = "google" class VertexAIAuthError(Exception): """Raised when there are authentication issues with Vertex AI.""" pass class VertexAIEstimatingTokenizer(EstimatingTokenizer): """Tokenizer that estimates token count for Vertex AI models.""" def __init__(self, client: google.genai.Client, model_name: str): self.model_name = model_name self._client = client if "claude" in model_name.lower(): self.encoding: tiktoken.Encoding | None = tiktoken.encoding_for_model( "gpt-4o-2024-08-06" ) else: self.encoding = None @override async def estimate_token_count(self, prompt: str) -> int: """Estimate token count using tiktoken for Claude, Google API for Gemini.""" if self.encoding: tokens = self.encoding.encode(prompt) return int(len(tokens) * 1.15) # @check - as seen on aws_service for bedrock else: model_approximation = { "text-embedding-004": "gemini-2.5-pro", }.get(self.model_name, self.model_name) result = await self._client.aio.models.count_tokens( model=model_approximation, contents=prompt, ) return int(result.total_tokens or 0) def get_model_provider(model_name: str) -> ModelProvider: """Determine the model provider based on model name.""" if "claude" in model_name.lower(): return ModelProvider.ANTHROPIC elif "gemini" in model_name.lower(): return ModelProvider.GOOGLE else: raise ValueError(f"Unknown model provider for model: {model_name}") class VertexAIClaudeSchematicGenerator(BaseSchematicGenerator[T]): """Schematic generator for Claude models via Vertex AI.""" supported_hints = ["temperature", "max_tokens", "top_p", "top_k"] def __init__( self, project_id: str, region: str, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self.project_id = project_id self.region = region self._client = AsyncAnthropicVertex( project_id=project_id, region=region, ) self._genai_client = google.genai.Client(project=project_id, location=region, vertexai=True) self._tokenizer = VertexAIEstimatingTokenizer(self._genai_client, model_name) @property @override def id(self) -> str: return f"vertex-ai/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: # Claude models support 200k tokens return 200_000 @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, RateLimitError, APIResponseValidationError, ), max_exceptions=3, wait_times=(1.0, 2.0, 4.0), ), retry(InternalServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Vertex LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() anthropic_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} t_start = time.time() try: response = await self._client.messages.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, max_tokens=hints.get("max_tokens", 8192), **anthropic_api_arguments, ) except RateLimitError: self.logger.error( "Vertex AI rate limit exceeded. Possible reasons:\n" "1. Your GCP project may have insufficient quota.\n" "2. The model may not be enabled in Vertex AI Model Garden.\n" "3. You might have exceeded the requests-per-minute limit.\n\n" "Recommended actions:\n" "- Check your Vertex AI quotas in the GCP Console.\n" "- Ensure the model is enabled in Vertex AI Model Garden.\n" "- Review IAM permissions for the service account.\n" "- Visit: https://console.cloud.google.com/vertex-ai/model-garden", ) raise except Exception as e: if "403" in str(e) or "permission" in str(e).lower(): self.logger.error( f"Permission denied accessing Vertex AI. Ensure:\n" f"1. ADC is properly configured (run 'gcloud auth application-default login')\n" f"2. The service account has 'Vertex AI User' role\n" f"3. The {self.model_name} model is enabled in Vertex AI Model Garden\n" f"Error: {e}" ) raise t_end = time.time() raw_content = response.content[0].text try: json_content = normalize_json_output(raw_content) json_object = jsonfinder.only_json(json_content)[2] except Exception: self.logger.error( f"Failed to extract JSON returned by {self.model_name}:\n{raw_content}" ) raise try: model_content = self.schema.model_validate(json_object) await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.input_tokens, output_tokens=response.usage.output_tokens, ) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.input_tokens, output_tokens=response.usage.output_tokens, ), ), ) except ValidationError: self.logger.error( f"JSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class VertexAIGeminiSchematicGenerator(BaseSchematicGenerator[T]): """Schematic generator for Gemini models""" supported_hints = ["temperature", "thinking_config"] def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, project_id: str, region: str, model_name: str, ) -> None: super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self.project_id = project_id self.region = region self._client = google.genai.Client(project=project_id, location=region, vertexai=True) self._tokenizer = VertexAIEstimatingTokenizer(self._client, model_name) @property @override def id(self) -> str: return f"vertex-ai/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: if "flash" in self.model_name.lower(): return 1024 * 1024 # 1M tokens else: return 2 * 1024 * 1024 # 2M tokens @policy( [ retry( exceptions=( NotFound, TooManyRequests, ResourceExhausted, ), max_exceptions=3, wait_times=(1.0, 2.0, 4.0), ), retry(ServerError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: with self.logger.scope(f"Vertex LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: if isinstance(prompt, PromptBuilder): prompt = prompt.build() gemini_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} config = { "response_mime_type": "application/json", "response_schema": self.schema.model_json_schema(), **gemini_api_arguments, } t_start = time.time() try: response = await self._client.aio.models.generate_content( model=self.model_name, contents=prompt, config=cast(google.genai.types.GenerateContentConfigOrDict, config), ) except TooManyRequests: self.logger.error( "Google API rate limit exceeded.\n\n" "Possible reasons:\n" "1. Insufficient API credits in your account.\n" "2. Using a free-tier account with limited request capacity.\n" "3. Exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Google API account balance and billing status.\n" "- Review your API usage limits in the Google Cloud Console.\n" "- Learn more about quotas and limits:\n" " https://cloud.google.com/docs/quota-and-billing/quotas/quotas-overview" ) raise except Exception as e: if "403" in str(e) or "permission" in str(e).lower(): self.logger.error( f"Permission denied accessing Google Gen AI. Ensure:\n" f"1. GEMINI_API_KEY is properly configured\n" f"2. The API key has proper permissions\n" f"3. The {self.model_name} model is accessible\n" f"Error: {e}" ) raise t_end = time.time() raw_content = response.text try: json_content = normalize_json_output(raw_content or "{}") # Fix Gemini's quote issues json_content = json_content.replace(""", '"').replace(""", '"') # Fix double-escaped sequences for control_char in "utn": json_content = json_content.replace(f"\\\\{control_char}", f"\\{control_char}") json_object = jsonfinder.only_json(json_content)[2] except Exception: self.logger.error(f"Failed to extract JSON from {self.model_name}:\n{raw_content}") raise if response.usage_metadata: self.logger.trace(response.usage_metadata.model_dump_json(indent=2)) try: model_content = self.schema.model_validate(json_object) return SchematicGenerationResult( content=model_content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage_metadata.prompt_token_count or 0, output_tokens=response.usage_metadata.candidates_token_count or 0, extra={ "cached_input_tokens": ( response.usage_metadata.cached_content_token_count if response.usage_metadata else 0 ) or 0 }, ) if response.usage_metadata else UsageInfo(input_tokens=0, output_tokens=0, extra={}), ), ) except ValidationError: self.logger.error(f"JSON from {self.model_name} doesn't match schema:\n{raw_content}") raise class VertexClaudeOpus4(VertexAIClaudeSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="claude-opus-4@20250514", logger=logger, tracer=tracer, meter=meter, ) class VertexClaudeSonnet4(VertexAIClaudeSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="claude-sonnet-4@20250514", logger=logger, tracer=tracer, meter=meter, ) class VertexClaudeSonnet35(VertexAIClaudeSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="claude-3-5-sonnet-v2@20241022", logger=logger, tracer=tracer, meter=meter, ) class VertexClaudeHaiku35(VertexAIClaudeSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="claude-3-5-haiku@20241022", logger=logger, tracer=tracer, meter=meter, ) class VertexGemini15Flash(VertexAIGeminiSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="gemini-1.5-flash", logger=logger, tracer=tracer, meter=meter, ) class VertexGemini15Pro(VertexAIGeminiSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="gemini-1.5-pro", logger=logger, tracer=tracer, meter=meter, ) class VertexGemini20Flash(VertexAIGeminiSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="gemini-2.0-flash", logger=logger, tracer=tracer, meter=meter, ) class VertexGemini25Flash(VertexAIGeminiSchematicGenerator[T]): def __init__( self, project_id: str, region: str, logger: Logger, tracer: Tracer, meter: Meter ) -> None: super().__init__( project_id=project_id, region=region, model_name="gemini-2.5-flash", logger=logger, tracer=tracer, meter=meter, ) @override async def generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: return await super().generate( prompt, {"thinking_config": {"thinking_budget": 0}, **hints}, ) class VertexGemini25Pro(VertexAIGeminiSchematicGenerator[T]): def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, project_id: str, region: str, ) -> None: super().__init__( logger=logger, tracer=tracer, meter=meter, project_id=project_id, region=region, model_name="gemini-2.5-pro", ) class VertexAIEmbedder(BaseEmbedder): """Embedder using Google Gen AI text embeddings""" supported_hints = ["title", "task_type"] def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, model_name: str, ): self.project_id = os.environ.get("VERTEX_AI_PROJECT_ID") if not self.project_id: raise ValueError( "VERTEX_AI_PROJECT_ID environment variable must be set. " "Set this to your Google Cloud Project ID." ) super().__init__(logger, tracer, meter, model_name) self.region = os.environ.get("VERTEX_AI_REGION", "us-central1") self._client = google.genai.Client( project=self.project_id, location=self.region, vertexai=True ) self._tokenizer = VertexAIEstimatingTokenizer(self._client, model_name) @property @override def id(self) -> str: return f"vertex-ai/{self.model_name}" @property @override def tokenizer(self) -> EstimatingTokenizer: return self._tokenizer @property @override def max_tokens(self) -> int: return 8192 @policy( [ retry( exceptions=( NotFound, TooManyRequests, ResourceExhausted, ), max_exceptions=3, wait_times=(1.0, 2.0, 4.0), ) ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: gemini_api_arguments = {k: v for k, v in hints.items() if k in self.supported_hints} if "task_type" not in gemini_api_arguments: gemini_api_arguments["task_type"] = "RETRIEVAL_DOCUMENT" try: response = await self._client.aio.models.embed_content( # type: ignore model=self.model_name, contents=texts, # type: ignore config=cast(google.genai.types.EmbedContentConfigDict, gemini_api_arguments), ) vectors = [ data_point.values for data_point in response.embeddings or [] if data_point.values ] return EmbeddingResult(vectors=vectors) except TooManyRequests: self.logger.error( ( "Google API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Google API account balance and billing status.\n" "- Review your API usage limits in Google's dashboard.\n" "- For more details on rate limits and usage tiers, visit:\n" " https://cloud.google.com/docs/quota-and-billing/quotas/quotas-overview" ), ) raise except Exception as e: self.logger.error(f"Error during embedding: {e}") raise class VertexTextEmbedding004(VertexAIEmbedder): def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: super().__init__(model_name="text-embedding-004", logger=logger, tracer=tracer, meter=meter) @property @override def dimensions(self) -> int: return 768 class VertexAIService(NLPService): """NLP Service for Vertex AI supporting both Claude and Gemini models via appropriate APIs.""" CLAUDE_MODELS = { "claude-opus-4": "claude-opus-4@20250514", "claude-sonnet-4": "claude-sonnet-4@20250514", "claude-sonnet-3.5": "claude-3-5-sonnet-v2@20241022", "claude-haiku-3.5": "claude-3-5-haiku@20241022", } GEMINI_MODELS = { "gemini-1.5-flash": "gemini-1.5-flash", "gemini-1.5-pro": "gemini-1.5-pro", "gemini-2.0-flash": "gemini-2.0-flash", "gemini-2.5-pro": "gemini-2.5-pro", "gemini-2.5-flash": "gemini-2.5-flash", } @staticmethod def verify_environment() -> str | None: """Returns an error message if the environment is not set up correctly.""" required_vars = { "VERTEX_AI_PROJECT_ID": "your-project-id", "VERTEX_AI_REGION": "us-central1", "VERTEX_AI_MODEL": "claude-sonnet-3.5", } missing_vars = [] for var_name, example_value in required_vars.items(): if not os.environ.get(var_name): missing_vars.append(f"export {var_name}={example_value}") if missing_vars: return f"""\ You're using the VERTEX AI service, but required environment variables are not set. Please set the following environment variables before running Parlant: {chr(10).join(missing_vars)} """ return None @staticmethod def validate_adc() -> str | None: """Validate that Application Default Credentials are configured.""" try: credentials, project = google.auth.default() # type: ignore if not credentials: return """\ No Application Default Credentials found. Run 'gcloud auth application-default login' for local development. """ except Exception as e: return f"""\ Failed to load Application Default Credentials: {e} Run 'gcloud auth application-default login' for local development. """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: self.project_id = os.environ.get("VERTEX_AI_PROJECT_ID", "project_id") self.region = os.environ.get("VERTEX_AI_REGION", "us-central1") self.model_name = self._normalize_model_name( os.environ.get("VERTEX_AI_MODEL", "claude-sonnet-3.5") ) self.logger = logger self._tracer = tracer self._meter = meter self.logger.info( f"Initialized VertexAIService with model {self.model_name} " f"in project {self.project_id}, region {self.project_id}" ) @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") def _normalize_model_name(self, model_name: str) -> str: """Normalize model name to full version string.""" # Check if it's a short name we recognize if model_name in self.CLAUDE_MODELS: return self.CLAUDE_MODELS[model_name] elif model_name in self.GEMINI_MODELS: return self.GEMINI_MODELS[model_name] # Otherwise assume it's already a full model name return model_name @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> SchematicGenerator[T]: """Get a schematic generator for the specified type.""" provider = get_model_provider(self.model_name) if provider == ModelProvider.ANTHROPIC: if "opus-4" in self.model_name: primary = VertexClaudeOpus4[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) fallback = VertexClaudeSonnet4[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) return FallbackSchematicGenerator[t]( # type: ignore primary, fallback, logger=self.logger ) elif "sonnet-4" in self.model_name: return VertexClaudeSonnet4[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) elif "claude-3-5" in self.model_name: return VertexClaudeSonnet35[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) elif "haiku" in self.model_name: return VertexClaudeHaiku35[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) else: # Default to Sonnet 3.5 return VertexClaudeSonnet35[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) elif provider == ModelProvider.GOOGLE: if "1.5-flash" in self.model_name: return VertexGemini15Flash[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) elif "1.5-pro" in self.model_name: return VertexGemini15Pro[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) elif "2.0-flash" in self.model_name: return VertexGemini20Flash[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) elif "2.5-flash" in self.model_name: return VertexGemini25Flash[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) elif "2.5-pro" in self.model_name: return VertexGemini25Pro[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) else: # Default to Gemini 2.5-flash return VertexGemini25Flash[t]( # type: ignore project_id=self.project_id, region=self.region, logger=self.logger, tracer=self._tracer, meter=self._meter, ) else: raise ValueError(f"Unsupported model: {self.model_name}") @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: """Get an embedder for text embeddings using Google Gen AI.""" return VertexTextEmbedding004(logger=self.logger, tracer=self._tracer, meter=self._meter) @override async def get_moderation_service(self) -> ModerationService: # @Todo - add moderation service """Get a moderation service.""" return NoModeration() ================================================ FILE: src/parlant/adapters/nlp/zhipu_service.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations from itertools import chain import time from parlant.core.engines.alpha.guideline_matching.generic.journey.journey_backtrack_node_selection import ( JourneyBacktrackNodeSelectionSchema, ) from zhipuai import ZhipuAI # type: ignore from zhipuai.core._errors import ( # type: ignore APIConnectionError, APITimeoutError, APIReachLimitError, APIServerFlowExceedError, APIInternalError, ) from typing import Any, Mapping from typing_extensions import override import json import jsonfinder # type: ignore import os from pydantic import ValidationError import tiktoken from parlant.adapters.nlp.common import normalize_json_output, record_llm_metrics from parlant.core.engines.alpha.canned_response_generator import ( CannedResponseDraftSchema, CannedResponseSelectionSchema, ) from parlant.core.engines.alpha.prompt_builder import PromptBuilder from parlant.core.engines.alpha.tool_calling.single_tool_batch import SingleToolBatchSchema from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.meter import Meter from parlant.core.nlp.policies import policy, retry from parlant.core.nlp.tokenization import EstimatingTokenizer from parlant.core.nlp.service import ( EmbedderHints, NLPService, SchematicGeneratorHints, StreamingTextGeneratorHints, ) from parlant.core.nlp.embedding import BaseEmbedder, Embedder, EmbeddingResult from parlant.core.nlp.generation import ( T, BaseSchematicGenerator, SchematicGenerationResult, StreamingTextGenerator, ) from parlant.core.nlp.generation_info import GenerationInfo, UsageInfo from parlant.core.nlp.moderation import ( BaseModerationService, CustomerModerationContext, ModerationCheck, ModerationTag, ) RATE_LIMIT_ERROR_MESSAGE = ( "Zhipu AI API rate limit exceeded. Possible reasons:\n" "1. Your account may have insufficient API credits.\n" "2. You may be using a free-tier account with limited request capacity.\n" "3. You might have exceeded the requests-per-minute limit for your account.\n\n" "Recommended actions:\n" "- Check your Zhipu AI account balance and billing status.\n" "- Review your API usage limits in Zhipu AI's dashboard.\n" "- For more details on rate limits and usage, visit:\n" " https://open.bigmodel.cn/dev/api\n" ) class ZhipuEstimatingTokenizer(EstimatingTokenizer): """Tokenizer for estimating token count for Zhipu AI models using tiktoken.""" def __init__(self, model_name: str) -> None: """Initialize the tokenizer with a model name. Args: model_name: The name of the Zhipu AI model (e.g., 'glm-4-plus') """ self.model_name = model_name # Use cl100k_base encoding as an approximation for Zhipu AI models self.encoding = tiktoken.get_encoding("cl100k_base") @override async def estimate_token_count(self, prompt: str) -> int: """Estimate the number of tokens in the given prompt. Args: prompt: The text to estimate token count for Returns: The estimated number of tokens """ tokens = self.encoding.encode(prompt) return len(tokens) class ZhipuSchematicGenerator(BaseSchematicGenerator[T]): """Base class for Zhipu AI schematic generators that produce structured JSON output.""" supported_zhipu_params = ["temperature", "max_tokens", "top_p"] supported_hints = supported_zhipu_params def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, tokenizer_model_name: str | None = None, ) -> None: """Initialize the Zhipu AI schematic generator. Args: model_name: The name of the Zhipu AI model (e.g., 'glm-4-plus') logger: Logger instance for logging operations meter: Meter instance for metrics tokenizer_model_name: Optional model name for tokenizer (defaults to model_name) """ super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = ZhipuAI(api_key=os.environ["ZHIPUAI_API_KEY"]) self._tokenizer = ZhipuEstimatingTokenizer( model_name=tokenizer_model_name or self.model_name ) @property @override def id(self) -> str: """Return the model identifier in the format 'zhipu/{model_name}'. Returns: The model identifier string """ return f"zhipu/{self.model_name}" @property @override def tokenizer(self) -> ZhipuEstimatingTokenizer: """Return the tokenizer instance. Returns: The ZhipuEstimatingTokenizer instance """ return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, APIReachLimitError, APIServerFlowExceedError, ), ), retry(APIInternalError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: """Generate structured JSON output using Zhipu AI model. Args: prompt: The prompt string or PromptBuilder instance hints: Optional parameters for generation (temperature, max_tokens, top_p) Returns: SchematicGenerationResult containing the parsed content and generation info """ with self.logger.scope(f"Zhipu LLM Request ({self.schema.__name__})"): return await self._do_generate(prompt, hints) async def _do_generate( self, prompt: str | PromptBuilder, hints: Mapping[str, Any] = {}, ) -> SchematicGenerationResult[T]: """Internal method to handle the actual API call and response processing. Args: prompt: The prompt string or PromptBuilder instance hints: Optional parameters for generation Returns: SchematicGenerationResult containing the parsed content and generation info """ # Build prompt if it's a PromptBuilder instance if isinstance(prompt, PromptBuilder): prompt = prompt.build() # Filter parameters to only include supported ones zhipu_api_arguments = {k: v for k, v in hints.items() if k in self.supported_zhipu_params} # Track response time t_start = time.time() try: response = self._client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model=self.model_name, response_format={"type": "json_object"}, **zhipu_api_arguments, ) except (APIReachLimitError, APIServerFlowExceedError): self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise t_end = time.time() # Log usage information if available if hasattr(response, "usage") and response.usage: self.logger.trace( f"Token usage - Input: {response.usage.prompt_tokens}, " f"Output: {response.usage.completion_tokens}, " f"Total: {response.usage.total_tokens}" ) # Extract raw content from response raw_content = response.choices[0].message.content or "{}" # Parse JSON from response try: json_content = json.loads(normalize_json_output(raw_content)) except json.JSONDecodeError: self.logger.warning(f"Invalid JSON returned by {self.model_name}:\n{raw_content})") json_content = jsonfinder.only_json(raw_content)[2] self.logger.warning("Found JSON content within model response; continuing...") # Validate against schema try: content = self.schema.model_validate(json_content) assert response.usage await record_llm_metrics( self.meter, self.model_name, schema_name=self.schema.__name__, input_tokens=response.usage.prompt_tokens or 0, output_tokens=response.usage.completion_tokens or 0, cached_input_tokens=0, ) return SchematicGenerationResult( content=content, info=GenerationInfo( schema_name=self.schema.__name__, model=self.id, duration=(t_end - t_start), usage=UsageInfo( input_tokens=response.usage.prompt_tokens or 0, output_tokens=response.usage.completion_tokens or 0, ), ), ) except ValidationError as e: self.logger.error( f"Error: {e.json(indent=2)}\nJSON content returned by {self.model_name} does not match expected schema:\n{raw_content}" ) raise class GLM_4_Plus(ZhipuSchematicGenerator[T]): """GLM-4-Plus model for high-performance tasks.""" def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: """Initialize GLM-4-Plus model. Args: logger: Logger instance for logging operations meter: Meter instance for metrics """ super().__init__(model_name="glm-4-plus", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: """Return the maximum token limit for GLM-4-Plus. Returns: Maximum token count of 128K """ return 128 * 1024 class GLM_4_Flash(ZhipuSchematicGenerator[T]): """GLM-4-Flash model for fast response tasks.""" def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: """Initialize GLM-4-Flash model. Args: logger: Logger instance for logging operations meter: Meter instance for metrics """ super().__init__(model_name="glm-4-flash", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: """Return the maximum token limit for GLM-4-Flash. Returns: Maximum token count of 128K """ return 128 * 1024 class GLM_4_Air(ZhipuSchematicGenerator[T]): """GLM-4-Air model for lightweight tasks.""" def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: """Initialize GLM-4-Air model. Args: logger: Logger instance for logging operations meter: Meter instance for metrics """ super().__init__(model_name="glm-4-air", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: """Return the maximum token limit for GLM-4-Air. Returns: Maximum token count of 128K """ return 128 * 1024 class ZhipuEmbedder(BaseEmbedder): """Embedder for generating text embeddings using Zhipu AI models.""" supported_arguments = ["dimensions"] def __init__( self, model_name: str, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: """Initialize the Zhipu AI embedder. Args: model_name: The name of the Zhipu AI embedding model (e.g., 'embedding-3') logger: Logger instance for logging operations meter: Meter instance for metrics """ super().__init__(logger=logger, tracer=tracer, meter=meter, model_name=model_name) self._client = ZhipuAI(api_key=os.environ["ZHIPUAI_API_KEY"]) self._tokenizer = ZhipuEstimatingTokenizer(model_name=self.model_name) @property @override def id(self) -> str: """Return the embedding model identifier in the format 'zhipu/{model_name}'. Returns: The model identifier string """ return f"zhipu/{self.model_name}" @property @override def tokenizer(self) -> ZhipuEstimatingTokenizer: """Return the tokenizer instance. Returns: The ZhipuEstimatingTokenizer instance """ return self._tokenizer @policy( [ retry( exceptions=( APIConnectionError, APITimeoutError, APIReachLimitError, APIServerFlowExceedError, ), ), retry(APIInternalError, max_exceptions=2, wait_times=(1.0, 5.0)), ] ) @override async def do_embed( self, texts: list[str], hints: Mapping[str, Any] = {}, ) -> EmbeddingResult: """Generate embeddings for the given texts using Zhipu AI embedding API. Args: texts: List of text strings to generate embeddings for hints: Optional parameters for embedding (dimensions) Returns: EmbeddingResult containing the list of embedding vectors """ # Filter parameters to only include supported ones zhipu_api_arguments = {k: v for k, v in hints.items() if k in self.supported_arguments} try: response = self._client.embeddings.create( model=self.model_name, input=texts, **zhipu_api_arguments, ) except (APIReachLimitError, APIServerFlowExceedError): self.logger.error(RATE_LIMIT_ERROR_MESSAGE) raise # Log usage information if available if hasattr(response, "usage") and response.usage: self.logger.trace(f"Token usage - Total: {response.usage.total_tokens}") # Extract embeddings from response embeddings = [item.embedding for item in response.data] return EmbeddingResult(vectors=embeddings) class Embedding_3(ZhipuEmbedder): """Embedding-3 model for generating text embeddings.""" def __init__(self, logger: Logger, tracer: Tracer, meter: Meter) -> None: """Initialize Embedding-3 model. Args: logger: Logger instance for logging operations meter: Meter instance for metrics """ super().__init__(model_name="embedding-3", logger=logger, tracer=tracer, meter=meter) @property @override def max_tokens(self) -> int: """Return the maximum token limit for Embedding-3. Returns: Maximum token count of 8192 """ return 8192 @property @override def dimensions(self) -> int: """Return the default embedding dimensions for Embedding-3. Returns: Default embedding dimensions of 2048 """ return 2048 class ZhipuModerationService(BaseModerationService): """Moderation service for detecting inappropriate content using Zhipu AI.""" def __init__(self, model_name: str, logger: Logger, meter: Meter) -> None: """Initialize the Zhipu AI moderation service. Args: model_name: The name of the Zhipu AI moderation model logger: Logger instance for logging operations meter: Meter instance for metrics """ super().__init__(logger, meter) self.model_name = model_name self._client = ZhipuAI(api_key=os.environ["ZHIPUAI_API_KEY"]) self._hist_moderation_request_duration = meter.create_duration_histogram( name="moderation", description="Duration of moderation requests in milliseconds", ) @override async def do_moderate(self, context: CustomerModerationContext) -> ModerationCheck: """Check content for inappropriate material using Zhipu AI moderation API. Args: context: The moderation context containing the message to check Returns: ModerationCheck object containing flagged status and tags """ async with self._hist_moderation_request_duration.measure(): return await self._do_moderate(context) async def _do_moderate(self, context: CustomerModerationContext) -> ModerationCheck: """Internal method to handle the actual moderation API call. Args: context: The moderation context containing the message to check Returns: ModerationCheck object containing flagged status and tags """ def extract_tags(category: str) -> list[ModerationTag]: """Map Zhipu AI moderation categories to ModerationTag values. Args: category: The Zhipu AI category name Returns: List of corresponding ModerationTag values """ mapping: dict[str, list[ModerationTag]] = { "sexual": ["sexual"], "hate": ["hate"], "harassment": ["harassment"], "violence": ["violence"], "self_harm": ["self-harm"], "self-harm": ["self-harm"], "illegal": ["illicit"], "illicit": ["illicit"], } return mapping.get(category.replace("-", "_"), []) response = self._client.moderations.create( model=self.model_name, input=context.message, ) result = response.results[0] return ModerationCheck( flagged=result.flagged, tags=list( set( chain.from_iterable( extract_tags(category) for category, detected in result.categories if detected ) ) ), ) class ZhipuService(NLPService): """Main NLP service class for Zhipu AI integration.""" @staticmethod def verify_environment() -> str | None: """Verify that the environment is properly configured for Zhipu AI service. Returns: Error message string if environment is not configured correctly, None otherwise """ if not os.environ.get("ZHIPUAI_API_KEY"): return """\ You're using the Zhipu AI NLP service, but ZHIPUAI_API_KEY is not set. Please set ZHIPUAI_API_KEY in your environment before running Parlant. To obtain an API key: 1. Visit https://open.bigmodel.cn/ 2. Register or log in to your account 3. Create an API key in the console 4. Set the environment variable: export ZHIPUAI_API_KEY=your_api_key_here """ return None def __init__( self, logger: Logger, tracer: Tracer, meter: Meter, ) -> None: """Initialize the Zhipu AI service. Args: logger: Logger instance for logging operations meter: Meter instance for metrics """ self._logger = logger self._tracer = tracer self._meter = meter self._logger.info("Initialized ZhipuService") @property @override def supports_streaming(self) -> bool: return False @override async def get_streaming_text_generator( self, hints: StreamingTextGeneratorHints = {} ) -> StreamingTextGenerator: raise NotImplementedError("Streaming is not supported. Check supports_streaming first.") @override async def get_schematic_generator( self, t: type[T], hints: SchematicGeneratorHints = {} ) -> ZhipuSchematicGenerator[T]: """Get the appropriate schematic generator for the given schema type. Args: t: The schema type to generate for Returns: A ZhipuSchematicGenerator instance configured for the schema type """ return { SingleToolBatchSchema: GLM_4_Flash[SingleToolBatchSchema], JourneyBacktrackNodeSelectionSchema: GLM_4_Plus[JourneyBacktrackNodeSelectionSchema], CannedResponseDraftSchema: GLM_4_Plus[CannedResponseDraftSchema], CannedResponseSelectionSchema: GLM_4_Plus[CannedResponseSelectionSchema], }.get(t, GLM_4_Flash[t])(self._logger, self._tracer, self._meter) # type: ignore @override async def get_embedder(self, hints: EmbedderHints = {}) -> Embedder: """Get the embedder instance for generating text embeddings. Returns: An Embedding_3 embedder instance """ return Embedding_3(self._logger, self._tracer, self._meter) @override async def get_moderation_service(self) -> BaseModerationService: """Get the moderation service instance for content checking. Returns: A ZhipuModerationService instance """ return ZhipuModerationService( model_name="moderation", logger=self._logger, meter=self._meter ) ================================================ FILE: src/parlant/adapters/tracing/opentelemetry.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import contextvars import os from contextlib import contextmanager from types import TracebackType from typing import Iterator, Mapping from typing_extensions import override, Self from opentelemetry import trace, context from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( OTLPSpanExporter as GrpcOTLPSpanExporter, ) from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( OTLPSpanExporter as HttpOTLPSpanExporter, ) from opentelemetry.trace import Status, StatusCode, SpanContext, TraceFlags from opentelemetry.trace.span import TraceState from parlant.core.common import generate_id from parlant.core.tracer import Tracer, AttributeValue class OpenTelemetryTracer(Tracer): def __init__(self) -> None: self._service_name = os.getenv("OTEL_SERVICE_NAME", "parlant") self._tracer_provider: TracerProvider self._span_processor: BatchSpanProcessor self._span_exporter: GrpcOTLPSpanExporter | HttpOTLPSpanExporter self._tracer: trace.Tracer self._spans = contextvars.ContextVar[str]( "otel_tracer_spans", default="", ) self._attributes = contextvars.ContextVar[Mapping[str, AttributeValue]]( "otel_tracer_attributes", default={}, ) self._trace_id = contextvars.ContextVar[str]( "otel_tracer_trace_id", default="", ) self._current_span = contextvars.ContextVar[trace.Span | None]( "otel_tracer_current_span", default=None, ) async def __aenter__(self) -> Self: resource = Resource.create({"service.name": self._service_name}) self._tracer_provider = TracerProvider(resource=resource) # Add console exporter for debugging (using BatchSpanProcessor) console_exporter = ConsoleSpanExporter() console_processor = BatchSpanProcessor( span_exporter=console_exporter, schedule_delay_millis=1000, ) self._tracer_provider.add_span_processor(console_processor) # Add OTLP exporter if endpoint is configured endpoint = os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") if endpoint: insecure = os.getenv("OTEL_EXPORTER_OTLP_INSECURE", "false").lower() == "true" protocol = os.getenv("OTEL_EXPORTER_OTLP_PROTOCOL", "grpc").lower() match protocol: case "http/protobuf": self._span_exporter = HttpOTLPSpanExporter(endpoint=endpoint) case "http/json": raise ValueError( "http/json protocol is not supported for traces exporter. please use http/protobuf or grpc." ) case "grpc": self._span_exporter = GrpcOTLPSpanExporter( endpoint=endpoint, insecure=insecure, ) case _: raise ValueError(f"Unsupported OTLP protocol: {protocol}") self._span_processor = BatchSpanProcessor( span_exporter=self._span_exporter, schedule_delay_millis=2000, ) self._tracer_provider.add_span_processor(self._span_processor) trace.set_tracer_provider(self._tracer_provider) self._tracer = trace.get_tracer(__name__) return self async def __aexit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, ) -> bool: self._tracer_provider.force_flush() self._tracer_provider.shutdown() return False @contextmanager @override def span( self, span_id: str, attributes: Mapping[str, AttributeValue] = {}, ) -> Iterator[None]: # Use standard OpenTelemetry span creation current_spans = self._spans.get() # Prepare attributes first current_attributes = self._attributes.get() new_attributes = {**current_attributes, **attributes} if not current_spans: new_spans = span_id custom_trace_id = generate_id({"strategy": "uuid4"}) trace_id_reset_token = self._trace_id.set(custom_trace_id) # Convert UUID hex to proper OpenTelemetry format # Ensure exactly 32 hex chars (128 bits) for trace ID trace_id_hex = str(custom_trace_id)[:32] trace_id_int = int(trace_id_hex, 16) # Ensure trace ID is non-zero (OpenTelemetry requirement) if trace_id_int == 0: trace_id_int = 1 # Generate 64-bit span ID (16 hex chars) span_uuid = generate_id({"strategy": "uuid4"}) span_id_hex = str(span_uuid)[:16] span_id_int = int(span_id_hex, 16) # Ensure span ID is non-zero (OpenTelemetry requirement) if span_id_int == 0: span_id_int = 1 span_context = SpanContext( trace_id=trace_id_int, span_id=span_id_int, is_remote=False, trace_flags=TraceFlags(0x01), trace_state=TraceState(), ) # For root spans, create a completely isolated context # We'll create the span with our custom context after setting up the isolated context isolated_ctx = context.Context() ctx = isolated_ctx else: new_spans = current_spans + f"::{span_id}" trace_id_reset_token = None ctx = context.get_current() spans_reset_token = self._spans.set(new_spans) attributes_reset_token = self._attributes.set(new_attributes) # Create span with the prepared context if not current_spans: # For root spans, we need to manually create a span with our custom context # Start the span normally first span = self._tracer.start_span(name=span_id, attributes=new_attributes, context=ctx) # Then update its context with our custom IDs (this is a workaround) if hasattr(span, "_context"): span._context = span_context else: # For child spans, create normally span = self._tracer.start_span(name=span_id, attributes=new_attributes, context=ctx) span_token = self._current_span.set(span) try: with trace.use_span(span, end_on_exit=True): yield except Exception as e: span.set_status(Status(StatusCode.ERROR, str(e))) span.record_exception(e) raise finally: self._spans.reset(spans_reset_token) self._attributes.reset(attributes_reset_token) self._current_span.reset(span_token) if trace_id_reset_token is not None: self._trace_id.reset(trace_id_reset_token) @contextmanager @override def attributes( self, attributes: Mapping[str, AttributeValue], ) -> Iterator[None]: current_attributes = self._attributes.get() new_attributes = {**current_attributes, **attributes} attributes_reset_token = self._attributes.set(new_attributes) current_span = self._current_span.get() if current_span and current_span.is_recording(): current_span.set_attributes(attributes) try: yield finally: self._attributes.reset(attributes_reset_token) @property @override def trace_id(self) -> str: if trace_id := self._trace_id.get(): return trace_id return "
" @property @override def span_id(self) -> str: if spans := self._spans.get(): return spans return "
" @override def get_attribute( self, name: str, ) -> AttributeValue | None: attributes = self._attributes.get() return attributes.get(name, None) @override def set_attribute( self, name: str, value: AttributeValue, ) -> None: current_attributes = self._attributes.get() new_attributes = {**current_attributes, name: value} self._attributes.set(new_attributes) current_span = self._current_span.get() if current_span and current_span.is_recording(): current_span.set_attribute(name, value) @override def add_event( self, name: str, attributes: Mapping[str, AttributeValue] = {}, ) -> None: current_span = self._current_span.get() if current_span and current_span.is_recording(): current_span.add_event(name, attributes) @override def flush(self) -> None: if hasattr(self, "_tracer_provider") and self._tracer_provider: self._tracer_provider.force_flush() ================================================ FILE: src/parlant/adapters/vector_db/chroma.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import json from pathlib import Path from typing import Any, Awaitable, Callable, Generic, Mapping, Optional, Sequence, cast from typing_extensions import override, Self import chromadb from chromadb.api.collection_configuration import ( CreateCollectionConfiguration, CreateHNSWConfiguration, ) from parlant.core.async_utils import ReaderWriterLock from parlant.core.common import JSONSerializable from parlant.core.loggers import Logger from parlant.core.tracer import Tracer from parlant.core.nlp.embedding import ( Embedder, EmbedderFactory, EmbeddingCacheProvider, NullEmbedder, ) from parlant.core.persistence.common import Where, ensure_is_total from parlant.core.persistence.vector_database import ( BaseDocument, BaseVectorCollection, DeleteResult, InsertResult, SimilarDocumentResult, UpdateResult, VectorDatabase, TDocument, identity_loader, ) class ChromaDatabase(VectorDatabase): def __init__( self, logger: Logger, tracer: Tracer, dir_path: Path, embedder_factory: EmbedderFactory, embedding_cache_provider: EmbeddingCacheProvider, ) -> None: self._dir_path = dir_path self._logger = logger self._tracer = tracer self._embedder_factory = embedder_factory self.chroma_client: chromadb.api.ClientAPI self._collections: dict[str, ChromaCollection[BaseDocument]] = {} self._embedding_cache_provider = embedding_cache_provider async def __aenter__(self) -> Self: self.chroma_client = chromadb.PersistentClient(str(self._dir_path)) return self async def __aexit__( self, exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[object], ) -> None: pass def format_collection_name( self, name: str, embedder_type: type[Embedder], ) -> str: return f"{name}_{embedder_type.__name__}" # Loads documents from unembedded collection, migrates them if needed, and ensures embedded collection is in sync async def _load_collection_documents( self, embedded_collection: chromadb.Collection, unembedded_collection: chromadb.Collection, embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> chromadb.Collection: failed_migrations: list[BaseDocument] = [] embedder = self._embedder_factory.create_embedder(embedder_type) unembedded_docs = unembedded_collection.get()["metadatas"] indexing_required = False if unembedded_docs: for doc in unembedded_docs: prospective_doc = cast(BaseDocument, doc) try: if loaded_doc := await document_loader(prospective_doc): if loaded_doc != prospective_doc: unembedded_collection.update( ids=[prospective_doc["id"]], documents=[loaded_doc["content"]], metadatas=[cast(chromadb.Metadata, loaded_doc)], embeddings=[0], ) indexing_required = True else: self._logger.warning(f'Failed to load document "{doc}"') unembedded_collection.delete(where={"id": prospective_doc["id"]}) failed_migrations.append(prospective_doc) except Exception as e: self._logger.error(f"Failed to load document '{doc}'. error: {e}.") failed_migrations.append(prospective_doc) # Store failed migrations in a separate collection for debugging if failed_migrations: failed_migrations_collection = await self.get_or_create_collection( "failed_migrations", BaseDocument, NullEmbedder, identity_loader, ) for failed_doc in failed_migrations: failed_migrations_collection.embedded_collection.add( ids=[failed_doc["id"]], documents=[failed_doc["content"]], metadatas=[cast(chromadb.Metadata, failed_doc)], embeddings=[0], ) if ( indexing_required or unembedded_collection.metadata["version"] != embedded_collection.metadata["version"] ): await self._index_collection(embedded_collection, unembedded_collection, embedder) return embedded_collection # Syncs embedded collection with unembedded collection async def _index_collection( self, collection: chromadb.Collection, unembedded_collection: chromadb.Collection, embedder: Embedder, ) -> None: if docs := unembedded_collection.get()["metadatas"]: unembedded_docs_by_id = {doc["id"]: doc for doc in docs} # Remove docs from embedded collection that no longer exist in unembedded # Update embeddings for changed docs if docs := collection.get()["metadatas"]: for doc in docs: if doc["id"] not in unembedded_docs_by_id: collection.delete(where={"id": cast(str, doc["id"])}) else: if doc["checksum"] != unembedded_docs_by_id[doc["id"]]["checksum"]: embeddings = list( ( await embedder.embed( [cast(str, unembedded_docs_by_id[doc["id"]]["content"])] ) ).vectors ) collection.update( ids=[str(doc["id"])], documents=[cast(str, unembedded_docs_by_id[doc["id"]]["content"])], metadatas=unembedded_docs_by_id[doc["id"]], embeddings=embeddings, ) unembedded_docs_by_id.pop(doc["id"]) # Add new docs from unembedded to embedded collection for doc in unembedded_docs_by_id.values(): collection.add( ids=[str(doc["id"])], documents=[cast(str, doc["content"])], metadatas=[doc], embeddings=list((await embedder.embed([cast(str, doc["content"])])).vectors), ) collection.metadata.update({"version": unembedded_collection.metadata["version"]}) @override async def create_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], ) -> ChromaCollection[TDocument]: if name in self._collections: raise ValueError(f'Collection "{name}" already exists.') embedded_collection = self.chroma_client.create_collection( name=self.format_collection_name(name, embedder_type), metadata={"version": 1}, embedding_function=None, configuration=CreateCollectionConfiguration( hnsw=CreateHNSWConfiguration(space="cosine") ), ) unembedded_collection = self.chroma_client.create_collection( name=f"{name}_unembedded", metadata={"version": 1}, embedding_function=None, ) self._collections[name] = ChromaCollection( self._logger, self._tracer, embedded_collection=embedded_collection, unembedded_collection=unembedded_collection, name=name, schema=schema, embedder=self._embedder_factory.create_embedder(embedder_type), embedding_cache_provider=self._embedding_cache_provider, version=1, ) return cast(ChromaCollection[TDocument], self._collections[name]) @override async def get_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> ChromaCollection[TDocument]: if collection := self._collections.get(name): return cast(ChromaCollection[TDocument], collection) # Find unembedded collection first which acts as the SSOT. # Check if we have a corresponding embedded collection for the embedder type. # Whether we find an existing embedded collection or create a new one, # we reindex and sync it with the unembedded collection to ensure consistency elif unembedded_collection := next( ( col for col in self.chroma_client.list_collections() if col.name == f"{name}_unembedded" ), None, ): embedded_collection = next( ( col for col in self.chroma_client.list_collections() if col.name == self.format_collection_name(name, embedder_type) ), None, ) or self.chroma_client.create_collection( name=self.format_collection_name(name, embedder_type), metadata={"version": 1}, embedding_function=None, configuration=CreateCollectionConfiguration( hnsw=CreateHNSWConfiguration(space="cosine") ), ) await self._index_collection( collection=embedded_collection, unembedded_collection=unembedded_collection, embedder=self._embedder_factory.create_embedder(embedder_type), ) self._collections[name] = ChromaCollection( self._logger, self._tracer, embedded_collection=await self._load_collection_documents( embedded_collection=embedded_collection, unembedded_collection=unembedded_collection, embedder_type=embedder_type, document_loader=document_loader, ), unembedded_collection=unembedded_collection, name=name, schema=schema, embedder=self._embedder_factory.create_embedder(embedder_type), embedding_cache_provider=self._embedding_cache_provider, version=1, ) return cast(ChromaCollection[TDocument], self._collections[name]) raise ValueError(f'ChromaDB collection "{name}" not found.') @override async def get_or_create_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> ChromaCollection[TDocument]: if collection := self._collections.get(name): return cast(ChromaCollection[TDocument], collection) # Get or create unembedded collection for storing raw documents # Then get or create embedded collection for storing embeddings # Load and migrate documents from unembedded collection, then reindex embedded collection to ensure it is in sync unembedded_collection = next( ( col for col in self.chroma_client.list_collections() if col.name == f"{name}_unembedded" ), None, ) or self.chroma_client.create_collection( name=f"{name}_unembedded", metadata={"version": 1}, embedding_function=None, ) embedded_collection = next( ( col for col in self.chroma_client.list_collections() if col.name == self.format_collection_name(name, embedder_type) ), None, ) or self.chroma_client.create_collection( name=self.format_collection_name(name, embedder_type), metadata={"version": 1}, configuration=CreateCollectionConfiguration( hnsw=CreateHNSWConfiguration(space="cosine") ), ) self._collections[name] = ChromaCollection( self._logger, self._tracer, embedded_collection=await self._load_collection_documents( embedded_collection=embedded_collection, unembedded_collection=unembedded_collection, embedder_type=embedder_type, document_loader=document_loader, ), unembedded_collection=unembedded_collection, name=name, schema=schema, embedder=self._embedder_factory.create_embedder(embedder_type), embedding_cache_provider=self._embedding_cache_provider, version=1, ) return cast(ChromaCollection[TDocument], self._collections[name]) @override async def delete_collection( self, name: str, ) -> None: if name not in self._collections: raise ValueError(f'Collection "{name}" not found.') self.chroma_client.delete_collection(name=name) self.chroma_client.delete_collection(name=f"{name}_unembedded") del self._collections[name] @override async def upsert_metadata( self, key: str, value: JSONSerializable, ) -> None: if metadata_collection := next( (col for col in self.chroma_client.list_collections() if col.name == "metadata"), None, ): pass else: metadata_collection = self.chroma_client.create_collection( name="metadata", embedding_function=None, ) if metadatas := metadata_collection.get()["metadatas"]: document = cast(dict[str, JSONSerializable], metadatas[0]) document[key] = value metadata_collection.update( ids=["__metadata__"], documents=["__metadata__"], metadatas=[cast(chromadb.Metadata, document)], embeddings=[0], ) else: document = {key: value} metadata_collection.add( ids=["__metadata__"], documents=["__metadata__"], metadatas=[cast(chromadb.Metadata, document)], embeddings=[0], ) @override async def remove_metadata( self, key: str, ) -> None: if metadata_collection := next( (col for col in self.chroma_client.list_collections() if col.name == "metadata"), None, ): if metadatas := metadata_collection.get()["metadatas"]: document = cast(dict[str, JSONSerializable], metadatas[0]) document.pop(key) metadata_collection.update( ids=["__metadata__"], documents=["__metadata__"], metadatas=[cast(chromadb.Metadata, document)], embeddings=[0], ) else: raise ValueError(f'Metadata with key "{key}" not found.') else: raise ValueError("Metadata collection not found.") @override async def read_metadata( self, ) -> Mapping[str, JSONSerializable]: if metadata_collection := next( (col for col in self.chroma_client.list_collections() if col.name == "metadata"), None, ): if metadatas := metadata_collection.get()["metadatas"]: return cast(dict[str, JSONSerializable], metadatas[0]) else: return {} else: return {} class ChromaCollection(Generic[TDocument], BaseVectorCollection[TDocument]): def __init__( self, logger: Logger, tracer: Tracer, embedded_collection: chromadb.Collection, unembedded_collection: chromadb.Collection, name: str, schema: type[TDocument], embedder: Embedder, embedding_cache_provider: EmbeddingCacheProvider, version: int, ) -> None: super().__init__(tracer) self._logger = logger self._tracer = tracer self._name = name self._schema = schema self._embedder = embedder self._embedding_cache_provider = embedding_cache_provider self._version = version self._lock = ReaderWriterLock() self._unembedded_collection = unembedded_collection self.embedded_collection = embedded_collection @override async def find( self, filters: Where, ) -> Sequence[TDocument]: async with self._lock.reader_lock: if metadatas := self.embedded_collection.get( where=cast(chromadb.Where, filters) or None )["metadatas"]: return [cast(TDocument, m) for m in metadatas] return [] @override async def find_one( self, filters: Where, ) -> Optional[TDocument]: async with self._lock.reader_lock: if metadatas := self.embedded_collection.get( where=cast(chromadb.Where, filters) or None )["metadatas"]: return cast(TDocument, {k: v for k, v in metadatas[0].items()}) return None @override async def insert_one( self, document: TDocument, ) -> InsertResult: ensure_is_total(document, self._schema) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[document["content"]], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([document["content"]])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[document["content"]], vectors=embeddings, ) async with self._lock.writer_lock: self._version += 1 self._unembedded_collection.add( ids=[document["id"]], documents=[document["content"]], metadatas=[cast(chromadb.Metadata, document)], embeddings=[0], ) self._unembedded_collection.modify( metadata={**self._unembedded_collection.metadata, **{"version": self._version}} ) self.embedded_collection.add( ids=[document["id"]], documents=[document["content"]], metadatas=[cast(chromadb.Metadata, document)], embeddings=embeddings, ) self.embedded_collection.modify( metadata={**self.embedded_collection.metadata, **{"version": self._version}} ) return InsertResult(acknowledged=True) @override async def update_one( self, filters: Where, params: TDocument, upsert: bool = False, ) -> UpdateResult[TDocument]: async with self._lock.writer_lock: if docs := self.embedded_collection.get(where=cast(chromadb.Where, filters) or None)[ "metadatas" ]: doc = docs[0] if "content" in params: content = params["content"] document = params["content"] else: content = str(doc["content"]) document = str(doc["content"]) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[content], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([content])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[content], vectors=embeddings, ) updated_document = {**doc, **params} self._version += 1 self._unembedded_collection.update( ids=[str(doc["id"])], documents=[document], metadatas=[cast(chromadb.Metadata, updated_document)], embeddings=[0], ) self._unembedded_collection.modify( metadata={**self._unembedded_collection.metadata, **{"version": self._version}} ) self.embedded_collection.update( ids=[str(doc["id"])], documents=[document], metadatas=[cast(chromadb.Metadata, updated_document)], embeddings=embeddings, # type: ignore ) self.embedded_collection.modify( metadata={**self.embedded_collection.metadata, **{"version": self._version}} ) return UpdateResult( acknowledged=True, matched_count=1, modified_count=1, updated_document=cast(TDocument, updated_document), ) elif upsert: ensure_is_total(params, self._schema) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[params["content"]], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([params["content"]])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[params["content"]], vectors=embeddings, ) self._version += 1 self._unembedded_collection.add( ids=[params["id"]], documents=[params["content"]], metadatas=[cast(chromadb.Metadata, params)], embeddings=[0], ) self._unembedded_collection.modify( metadata={**self._unembedded_collection.metadata, **{"version": self._version}} ) self.embedded_collection.add( ids=[params["id"]], documents=[params["content"]], metadatas=[cast(chromadb.Metadata, params)], embeddings=embeddings, ) self.embedded_collection.modify( metadata={**self.embedded_collection.metadata, **{"version": self._version}} ) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=params, ) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=None, ) @override async def delete_one( self, filters: Where, ) -> DeleteResult[TDocument]: async with self._lock.writer_lock: if docs := self.embedded_collection.get(where=cast(chromadb.Where, filters) or None)[ "metadatas" ]: if len(docs) > 1: raise ValueError( f"ChromaCollection delete_one: detected more than one document with filters '{filters}'. Aborting..." ) deleted_document = docs[0] self._version += 1 self._unembedded_collection.delete(where=cast(chromadb.Where, filters) or None) self._unembedded_collection.modify( metadata={**self._unembedded_collection.metadata, **{"version": self._version}} ) self.embedded_collection.delete(where=cast(chromadb.Where, filters) or None) self.embedded_collection.modify( metadata={**self.embedded_collection.metadata, **{"version": self._version}} ) return DeleteResult( deleted_count=1, acknowledged=True, deleted_document=cast(TDocument, deleted_document), ) return DeleteResult( acknowledged=True, deleted_count=0, deleted_document=None, ) @override async def do_find_similar_documents( self, filters: Where, query: str, k: int, hints: Mapping[str, Any] = {}, ) -> Sequence[SimilarDocumentResult[TDocument]]: async with self._lock.reader_lock: query_embeddings = list((await self._embedder.embed([query], hints)).vectors) docs = self.embedded_collection.query( where=cast(chromadb.Where, filters) or None, query_embeddings=query_embeddings, n_results=k, ) if not docs["metadatas"]: return [] self._logger.trace( f"Similar documents found\n{json.dumps(docs['metadatas'][0], indent=2)}" ) assert docs["distances"] return [ SimilarDocumentResult(document=cast(TDocument, m), distance=d) for m, d in zip(docs["metadatas"][0], docs["distances"][0]) ] ================================================ FILE: src/parlant/adapters/vector_db/qdrant.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import asyncio import gc import hashlib import json import sys from pathlib import Path from typing import Any, Awaitable, Callable, Generic, Mapping, Optional, Sequence, TypeVar, cast from typing_extensions import override, Self from qdrant_client import QdrantClient # type: ignore[import-untyped] from qdrant_client.http import models # type: ignore[import-untyped] from qdrant_client.http.models import Filter, FieldCondition, Range, MatchValue, MatchAny # type: ignore[import-untyped] from qdrant_client.http.exceptions import ResponseHandlingException # type: ignore[import-untyped] from parlant.core.async_utils import ReaderWriterLock from parlant.core.common import JSONSerializable from parlant.core.loggers import Logger from parlant.core.nlp.embedding import ( Embedder, EmbedderFactory, EmbeddingCacheProvider, NullEmbedder, ) from parlant.core.persistence.common import Where, ensure_is_total from parlant.core.persistence.vector_database import ( BaseDocument, BaseVectorCollection, DeleteResult, InsertResult, SimilarDocumentResult, UpdateResult, VectorDatabase, TDocument, identity_loader, ) from parlant.core.tracer import Tracer T = TypeVar("T") async def _retry_on_timeout_async( operation: Callable[[], Awaitable[T]], max_retries: int = 3, base_delay: float = 1.0, logger: Optional[Logger] = None, ) -> T: """ Retry an async operation on timeout errors with exponential backoff. Args: operation: The async operation to retry (callable that returns Awaitable[T]) max_retries: Maximum number of retry attempts base_delay: Base delay in seconds for exponential backoff logger: Optional logger for warning messages Returns: The result of the operation Raises: The last exception if all retries fail """ last_exception: Exception | None = None for attempt in range(max_retries): try: return await operation() except (ResponseHandlingException, Exception) as e: # Check if it's a timeout error error_str = str(e).lower() is_timeout = ( "timeout" in error_str or "read operation timed out" in error_str or "readtimeout" in error_str ) if is_timeout and attempt < max_retries - 1: delay = base_delay * (2**attempt) # Exponential backoff: 1s, 2s, 4s if logger: logger.warning( f"Qdrant operation timed out (attempt {attempt + 1}/{max_retries}). " f"Retrying in {delay}s..." ) await asyncio.sleep(delay) last_exception = e continue else: # Not a timeout or out of retries raise # Should never reach here, but just in case if last_exception: raise last_exception raise RuntimeError("Retry logic failed unexpectedly") def _string_id_to_int(doc_id: str) -> int: """Convert a string ID to an integer for Qdrant point IDs.""" # Use hash to convert string to integer # Take absolute value and use modulo to ensure it fits in int64 range hash_value = int(hashlib.sha256(doc_id.encode()).hexdigest()[:15], 16) # Ensure it's within safe int64 range (Qdrant supports int64) return hash_value % (2**63 - 1) def _extract_field_names_from_where(where: Where, field_names: set[str]) -> None: """Recursively extract all field names from a Where filter.""" if not where: return # Handle logical operators if "$and" in where: for sub_filter in where["$and"]: if isinstance(sub_filter, dict): _extract_field_names_from_where(sub_filter, field_names) return if "$or" in where: for sub_filter in where["$or"]: if isinstance(sub_filter, dict): _extract_field_names_from_where(sub_filter, field_names) return # Handle field conditions for field_name, field_filter in where.items(): if isinstance(field_filter, dict): # This is a field with operators field_names.add(field_name) # Recursively check nested filters (for complex nested structures) for operator, filter_value in field_filter.items(): if operator in ["$and", "$or"] and isinstance(filter_value, list): for nested_filter in filter_value: if isinstance(nested_filter, dict): _extract_field_names_from_where(nested_filter, field_names) def _convert_where_to_qdrant_filter(where: Where) -> Optional[Filter]: """Convert a Where filter to a Qdrant Filter.""" if not where: return None # Handle logical operators if "$and" in where: and_conditions: list[Filter] = [] for sub_filter in where["$and"]: if isinstance(sub_filter, dict): qdrant_filter = _convert_where_to_qdrant_filter(sub_filter) if qdrant_filter: and_conditions.append(qdrant_filter) if and_conditions: return Filter(must=and_conditions) return None if "$or" in where: or_conditions: list[Filter] = [] for sub_filter in where["$or"]: if isinstance(sub_filter, dict): qdrant_filter = _convert_where_to_qdrant_filter(sub_filter) if qdrant_filter: or_conditions.append(qdrant_filter) if or_conditions: return Filter(should=or_conditions) return None # Handle field conditions field_conditions: list[FieldCondition] = [] for field_name, field_filter in where.items(): if isinstance(field_filter, dict): for operator, filter_value in field_filter.items(): if operator == "$eq": field_conditions.append( FieldCondition(key=field_name, match=MatchValue(value=filter_value)) ) elif operator == "$ne": # Qdrant doesn't have $ne, so we use must_not return Filter( must_not=[ FieldCondition(key=field_name, match=MatchValue(value=filter_value)) ] ) elif operator == "$gt": field_conditions.append( FieldCondition(key=field_name, range=Range(gt=filter_value)) ) elif operator == "$gte": field_conditions.append( FieldCondition(key=field_name, range=Range(gte=filter_value)) ) elif operator == "$lt": field_conditions.append( FieldCondition(key=field_name, range=Range(lt=filter_value)) ) elif operator == "$lte": field_conditions.append( FieldCondition(key=field_name, range=Range(lte=filter_value)) ) elif operator == "$in": field_conditions.append( FieldCondition(key=field_name, match=MatchAny(any=list(filter_value))) ) elif operator == "$nin": # Qdrant doesn't have $nin, so we use must_not with MatchAny return Filter( must_not=[ FieldCondition(key=field_name, match=MatchAny(any=list(filter_value))) ] ) if field_conditions: return Filter(must=field_conditions) # type: ignore[arg-type] return None class QdrantDatabase(VectorDatabase): def __init__( self, logger: Logger, tracer: Tracer, path: Optional[Path] = None, url: Optional[str] = None, api_key: Optional[str] = None, embedder_factory: Optional[EmbedderFactory] = None, embedding_cache_provider: Optional[EmbeddingCacheProvider] = None, ) -> None: self._path = path self._url = url self._api_key = api_key self._logger = logger self._tracer = tracer self._embedder_factory = embedder_factory self.qdrant_client: Optional[QdrantClient] = None self._collections: dict[str, QdrantCollection[BaseDocument]] = {} self._embedding_cache_provider = embedding_cache_provider async def __aenter__(self) -> Self: if self._path: # On Windows, retry if the storage folder is locked (from previous instance) # This handles cases where a previous instance hasn't fully released file locks max_retries = 5 if sys.platform == "win32" else 1 for attempt in range(max_retries): try: self.qdrant_client = QdrantClient(path=str(self._path)) break except RuntimeError as e: if "already accessed" in str(e) and attempt < max_retries - 1: import asyncio # Exponential backoff: 0.05s, 0.1s, 0.15s, 0.2s, 0.25s delay = 0.05 * (attempt + 1) await asyncio.sleep(delay) continue raise elif self._url: # Set longer timeout for cloud operations (60 seconds) # This helps with large batch operations and slow network connections self.qdrant_client = QdrantClient( url=self._url, api_key=self._api_key, timeout=60, # 60 second timeout for cloud operations ) else: # Default to in-memory for testing self.qdrant_client = QdrantClient(":memory:") return self async def __aexit__( self, exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[object], ) -> None: # Close collections first to release any resources self._collections.clear() # Close Qdrant client to release file locks (important on Windows) if self.qdrant_client is not None: try: # Explicitly close the client to release file locks and resources # This is critical on Windows where file locks can persist # The close() method releases all file handles and locks self.qdrant_client.close() except AttributeError: # If close() doesn't exist (shouldn't happen, but be safe) pass except Exception as e: # Log but don't fail if close() raises an exception self._logger.warning(f"Error closing Qdrant client: {e}") finally: # Clear the reference and force garbage collection # This ensures all Python references are released client = self.qdrant_client self.qdrant_client = None del client # Only force GC on Windows where file locks are more persistent if sys.platform == "win32": gc.collect() # On Windows, file locks may take a moment to be released by the OS # Even after close(), Windows may need a brief moment to release locks import asyncio await asyncio.sleep(0.05) # Minimal delay for Windows file lock release def format_collection_name( self, name: str, embedder_type: type[Embedder], ) -> str: return f"{name}_{embedder_type.__name__}" def _ensure_payload_index(self, collection_name: str, field_name: str) -> None: """Ensure a payload index exists for a field.""" assert self.qdrant_client is not None, "Qdrant client must be initialized" try: # Check if index exists collection_info = self.qdrant_client.get_collection(collection_name) existing_indexes = collection_info.payload_schema or {} # Create index if it doesn't exist if field_name not in existing_indexes: self.qdrant_client.create_payload_index( collection_name=collection_name, field_name=field_name, field_schema=models.PayloadSchemaType.KEYWORD, ) except Exception: # Try to create index anyway (might fail if it exists) try: self.qdrant_client.create_payload_index( collection_name=collection_name, field_name=field_name, field_schema=models.PayloadSchemaType.KEYWORD, ) except Exception: pass # Index might already exist or creation failed # Loads documents from unembedded collection, migrates them if needed, and ensures embedded collection is in sync async def _load_collection_documents( self, embedded_collection_name: str, unembedded_collection_name: str, embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> str: assert self.qdrant_client is not None, "Qdrant client must be initialized" assert self._embedder_factory is not None, "Embedder factory must be provided" failed_migrations: list[BaseDocument] = [] embedder = self._embedder_factory.create_embedder(embedder_type) # Get all points from unembedded collection unembedded_points = self.qdrant_client.scroll( collection_name=unembedded_collection_name, limit=10000, with_payload=True, with_vectors=False, )[0] indexing_required = False if unembedded_points: for point in unembedded_points: prospective_doc = cast(BaseDocument, point.payload) try: if loaded_doc := await document_loader(prospective_doc): if loaded_doc != prospective_doc: # Update the unembedded collection self.qdrant_client.upsert( collection_name=unembedded_collection_name, points=[ models.PointStruct( id=point.id, vector=[0], payload=cast(dict[str, Any], loaded_doc), ) ], ) indexing_required = True else: self._logger.warning(f'Failed to load document "{prospective_doc}"') self.qdrant_client.delete( collection_name=unembedded_collection_name, points_selector=models.PointIdsList( points=[point.id], ), ) failed_migrations.append(prospective_doc) except Exception as e: self._logger.error(f"Failed to load document '{prospective_doc}'. error: {e}.") failed_migrations.append(prospective_doc) # Store failed migrations in a separate collection for debugging if failed_migrations: failed_migrations_collection = await self.get_or_create_collection( "failed_migrations", BaseDocument, NullEmbedder, identity_loader, ) for failed_doc in failed_migrations: # Use the collection interface consistently instead of direct Qdrant operations await failed_migrations_collection.insert_one(failed_doc) # Get version from special version point in collections unembedded_version = await self._get_collection_version(unembedded_collection_name) embedded_version = await self._get_collection_version(embedded_collection_name) if indexing_required or unembedded_version != embedded_version: await self._index_collection( embedded_collection_name, unembedded_collection_name, embedder ) return embedded_collection_name async def _get_collection_version(self, collection_name: str) -> int: """Get version from metadata collection.""" assert self.qdrant_client is not None, "Qdrant client must be initialized" version_key = f"{collection_name}_version" try: metadata = await self.read_metadata() return cast(int, metadata.get(version_key, 1)) except Exception: return 1 async def _set_collection_version(self, collection_name: str, version: int) -> None: """Set version in metadata collection.""" assert self.qdrant_client is not None, "Qdrant client must be initialized" version_key = f"{collection_name}_version" await self.upsert_metadata(version_key, version) # Syncs embedded collection with unembedded collection async def _index_collection( self, embedded_collection_name: str, unembedded_collection_name: str, embedder: Embedder, ) -> None: assert self.qdrant_client is not None, "Qdrant client must be initialized" # Get all points from unembedded collection unembedded_points = self.qdrant_client.scroll( collection_name=unembedded_collection_name, limit=10000, with_payload=True, with_vectors=False, )[0] # Map by document ID (string) from payload, not point ID (integer) unembedded_docs_by_id = { cast(str, point.payload["id"]): point for point in unembedded_points if point.payload is not None and "id" in point.payload } # Get all points from embedded collection embedded_points = self.qdrant_client.scroll( collection_name=embedded_collection_name, limit=10000, with_payload=True, with_vectors=True, )[0] # Map by document ID (string) from payload, not point ID (integer) embedded_docs_by_id = { cast(str, point.payload["id"]): point for point in embedded_points if point.payload is not None and "id" in point.payload } # Remove docs from embedded collection that no longer exist in unembedded # Update embeddings for changed docs for doc_id, embedded_point in embedded_docs_by_id.items(): if doc_id not in unembedded_docs_by_id: self.qdrant_client.delete( collection_name=embedded_collection_name, points_selector=models.PointIdsList(points=[embedded_point.id]), ) else: unembedded_point = unembedded_docs_by_id[doc_id] unembedded_doc = unembedded_point.payload if unembedded_doc is not None and embedded_point.payload is not None: # Only recompute embeddings if checksum changed if embedded_point.payload.get("checksum") != unembedded_doc.get("checksum"): embeddings = list( (await embedder.embed([cast(str, unembedded_doc["content"])])).vectors ) if not embeddings or len(embeddings[0]) == 0: self._logger.warning( f"Empty embedding for document {doc_id}, skipping sync" ) continue vector = embeddings[0] else: # Use existing vector if checksum hasn't changed # Cast to list[float] since we're using single vector collections vector = cast(list[float], embedded_point.vector) self.qdrant_client.upsert( collection_name=embedded_collection_name, points=[ models.PointStruct( id=embedded_point.id, # Keep existing point ID vector=vector, payload=unembedded_doc, ) ], ) unembedded_docs_by_id.pop(doc_id) # Add new docs from unembedded to embedded collection for doc_id, unembedded_point in unembedded_docs_by_id.items(): doc = unembedded_point.payload if doc is None: continue doc_dict = doc embeddings = list((await embedder.embed([cast(str, doc_dict["content"])])).vectors) if not embeddings or len(embeddings[0]) == 0: self._logger.warning(f"Empty embedding for document {doc_id}, skipping") continue # Convert string ID to integer for Qdrant point_id = _string_id_to_int(str(doc_id)) self.qdrant_client.upsert( collection_name=embedded_collection_name, points=[ models.PointStruct( id=point_id, vector=embeddings[0], payload=doc_dict, ) ], ) # Update version in unembedded collection unembedded_version = await self._get_collection_version(unembedded_collection_name) await self._set_collection_version(unembedded_collection_name, unembedded_version) await self._set_collection_version(embedded_collection_name, unembedded_version) @override async def create_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], ) -> QdrantCollection[TDocument]: assert self.qdrant_client is not None, "Qdrant client must be initialized" assert self._embedder_factory is not None, "Embedder factory must be provided" assert self._embedding_cache_provider is not None, ( "Embedding cache provider must be provided" ) if name in self._collections: raise ValueError(f'Collection "{name}" already exists.') embedder = self._embedder_factory.create_embedder(embedder_type) vector_size = embedder.dimensions embedded_collection_name = self.format_collection_name(name, embedder_type) unembedded_collection_name = f"{name}_unembedded" # Create embedded collection self.qdrant_client.create_collection( collection_name=embedded_collection_name, vectors_config=models.VectorParams( size=vector_size, distance=models.Distance.COSINE, ), ) # Create unembedded collection (with empty vectors for metadata storage) self.qdrant_client.create_collection( collection_name=unembedded_collection_name, vectors_config=models.VectorParams( size=1, # Minimal size for unembedded collection distance=models.Distance.COSINE, ), ) # Ensure payload indexes exist self._ensure_payload_index(embedded_collection_name, "id") self._ensure_payload_index(unembedded_collection_name, "id") collection = QdrantCollection( self._logger, self._tracer, qdrant_client=self.qdrant_client, embedded_collection_name=embedded_collection_name, unembedded_collection_name=unembedded_collection_name, name=name, schema=schema, embedder=embedder, embedding_cache_provider=self._embedding_cache_provider, version=1, ) collection._database = self self._collections[name] = collection # type: ignore[assignment] return collection # type: ignore[return-value] @override async def get_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> QdrantCollection[TDocument]: assert self.qdrant_client is not None, "Qdrant client must be initialized" assert self._embedder_factory is not None, "Embedder factory must be provided" assert self._embedding_cache_provider is not None, ( "Embedding cache provider must be provided" ) if collection := self._collections.get(name): return cast(QdrantCollection[TDocument], collection) # Find unembedded collection first which acts as the SSOT. unembedded_collection_name = f"{name}_unembedded" embedded_collection_name = self.format_collection_name(name, embedder_type) # Check if collections exist collections = self.qdrant_client.get_collections().collections collection_names = [col.name for col in collections] if unembedded_collection_name in collection_names: if embedded_collection_name not in collection_names: # Create embedded collection if it doesn't exist embedder = self._embedder_factory.create_embedder(embedder_type) self.qdrant_client.create_collection( collection_name=embedded_collection_name, vectors_config=models.VectorParams( size=embedder.dimensions, distance=models.Distance.COSINE, ), ) # Ensure payload index exists self._ensure_payload_index(embedded_collection_name, "id") await self._index_collection( embedded_collection_name=embedded_collection_name, unembedded_collection_name=unembedded_collection_name, embedder=self._embedder_factory.create_embedder(embedder_type), ) collection = QdrantCollection( self._logger, self._tracer, qdrant_client=self.qdrant_client, embedded_collection_name=await self._load_collection_documents( embedded_collection_name=embedded_collection_name, unembedded_collection_name=unembedded_collection_name, embedder_type=embedder_type, document_loader=document_loader, ), unembedded_collection_name=unembedded_collection_name, name=name, schema=schema, embedder=self._embedder_factory.create_embedder(embedder_type), embedding_cache_provider=self._embedding_cache_provider, version=1, ) collection._database = self self._collections[name] = collection # type: ignore[assignment] return collection # type: ignore[return-value] raise ValueError(f'Qdrant collection "{name}" not found.') @override async def get_or_create_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> QdrantCollection[TDocument]: assert self.qdrant_client is not None, "Qdrant client must be initialized" assert self._embedder_factory is not None, "Embedder factory must be provided" assert self._embedding_cache_provider is not None, ( "Embedding cache provider must be provided" ) if collection := self._collections.get(name): return cast(QdrantCollection[TDocument], collection) embedder = self._embedder_factory.create_embedder(embedder_type) vector_size = embedder.dimensions embedded_collection_name = self.format_collection_name(name, embedder_type) unembedded_collection_name = f"{name}_unembedded" # Get or create collections collections = self.qdrant_client.get_collections().collections collection_names = [col.name for col in collections] if unembedded_collection_name not in collection_names: self.qdrant_client.create_collection( collection_name=unembedded_collection_name, vectors_config=models.VectorParams( size=1, # Minimal size for unembedded collection distance=models.Distance.COSINE, ), ) if embedded_collection_name not in collection_names: self.qdrant_client.create_collection( collection_name=embedded_collection_name, vectors_config=models.VectorParams( size=vector_size, distance=models.Distance.COSINE, ), ) # Ensure payload indexes exist for both collections self._ensure_payload_index(unembedded_collection_name, "id") self._ensure_payload_index(embedded_collection_name, "id") collection = QdrantCollection( self._logger, self._tracer, qdrant_client=self.qdrant_client, embedded_collection_name=await self._load_collection_documents( embedded_collection_name=embedded_collection_name, unembedded_collection_name=unembedded_collection_name, embedder_type=embedder_type, document_loader=document_loader, ), unembedded_collection_name=unembedded_collection_name, name=name, schema=schema, embedder=embedder, embedding_cache_provider=self._embedding_cache_provider, version=1, ) collection._database = self self._collections[name] = collection # type: ignore[assignment] return collection # type: ignore[return-value] @override async def delete_collection( self, name: str, ) -> None: assert self.qdrant_client is not None, "Qdrant client must be initialized" if name not in self._collections: raise ValueError(f'Collection "{name}" not found.') embedded_collection_name = self.format_collection_name( name, type(self._collections[name]._embedder) ) unembedded_collection_name = f"{name}_unembedded" self.qdrant_client.delete_collection(collection_name=embedded_collection_name) self.qdrant_client.delete_collection(collection_name=unembedded_collection_name) del self._collections[name] @override async def upsert_metadata( self, key: str, value: JSONSerializable, ) -> None: assert self.qdrant_client is not None, "Qdrant client must be initialized" metadata_collection_name = "metadata" # Check if metadata collection exists collections = self.qdrant_client.get_collections().collections collection_names = [col.name for col in collections] if metadata_collection_name not in collection_names: self.qdrant_client.create_collection( collection_name=metadata_collection_name, vectors_config=models.VectorParams( size=1, distance=models.Distance.COSINE, ), ) # Get existing metadata points = self.qdrant_client.scroll( collection_name=metadata_collection_name, limit=1, with_payload=True, with_vectors=False, )[0] if points: document = cast(dict[str, JSONSerializable], points[0].payload) document[key] = value self.qdrant_client.upsert( collection_name=metadata_collection_name, points=[ models.PointStruct( id=points[0].id, vector=[0], payload=cast(dict[str, Any], document), ) ], ) else: document = {key: value} metadata_point_id = _string_id_to_int("__metadata__") self.qdrant_client.upsert( collection_name=metadata_collection_name, points=[ models.PointStruct( id=metadata_point_id, vector=[0], payload=cast(dict[str, Any], document), ) ], ) @override async def remove_metadata( self, key: str, ) -> None: assert self.qdrant_client is not None, "Qdrant client must be initialized" metadata_collection_name = "metadata" collections = self.qdrant_client.get_collections().collections collection_names = [col.name for col in collections] if metadata_collection_name in collection_names: points = self.qdrant_client.scroll( collection_name=metadata_collection_name, limit=1, with_payload=True, with_vectors=False, )[0] if points: document = cast(dict[str, JSONSerializable], points[0].payload) document.pop(key) self.qdrant_client.upsert( collection_name=metadata_collection_name, points=[ models.PointStruct( id=points[0].id, vector=[0], payload=cast(dict[str, Any], document), ) ], ) else: raise ValueError(f'Metadata with key "{key}" not found.') else: raise ValueError("Metadata collection not found.") @override async def read_metadata( self, ) -> Mapping[str, JSONSerializable]: assert self.qdrant_client is not None, "Qdrant client must be initialized" metadata_collection_name = "metadata" collections = self.qdrant_client.get_collections().collections collection_names = [col.name for col in collections] if metadata_collection_name in collection_names: points = self.qdrant_client.scroll( collection_name=metadata_collection_name, limit=1, with_payload=True, with_vectors=False, )[0] if points: return cast(dict[str, JSONSerializable], points[0].payload) else: return {} else: return {} class QdrantCollection(Generic[TDocument], BaseVectorCollection[TDocument]): def __init__( self, logger: Logger, tracer: Tracer, qdrant_client: QdrantClient, embedded_collection_name: str, unembedded_collection_name: str, name: str, schema: type[TDocument], embedder: Embedder, embedding_cache_provider: EmbeddingCacheProvider, version: int, ) -> None: super().__init__(tracer) self._logger = logger self._tracer = tracer self._name = name self._schema = schema self._embedder = embedder self._embedding_cache_provider = embedding_cache_provider self._version = version self._lock = ReaderWriterLock() self._unembedded_collection_name = unembedded_collection_name self.embedded_collection_name = embedded_collection_name self.qdrant_client = qdrant_client self._database: Optional[QdrantDatabase] = ( None # Reference to parent database for version methods ) @override async def find( self, filters: Where, ) -> Sequence[TDocument]: async with self._lock.reader_lock: # Ensure indexes exist for all fields used in filtering if filters and self._database: field_names: set[str] = set() _extract_field_names_from_where(filters, field_names) for field_name in field_names: self._database._ensure_payload_index(self.embedded_collection_name, field_name) qdrant_filter = _convert_where_to_qdrant_filter(filters) try: points = self.qdrant_client.scroll( collection_name=self.embedded_collection_name, scroll_filter=qdrant_filter, limit=10000, with_payload=True, with_vectors=False, )[0] except Exception: # If filter fails due to missing index, scroll all and filter in memory if qdrant_filter: all_points = self.qdrant_client.scroll( collection_name=self.embedded_collection_name, limit=10000, with_payload=True, with_vectors=False, )[0] # Filter in memory from parlant.core.persistence.common import matches_filters points = [ p for p in all_points if p.payload is not None and matches_filters(filters, p.payload) ] else: points = [] return [cast(TDocument, point.payload) for point in points] @override async def find_one( self, filters: Where, ) -> Optional[TDocument]: async with self._lock.reader_lock: # Ensure indexes exist for all fields used in filtering if filters and self._database: field_names: set[str] = set() _extract_field_names_from_where(filters, field_names) for field_name in field_names: self._database._ensure_payload_index(self.embedded_collection_name, field_name) qdrant_filter = _convert_where_to_qdrant_filter(filters) try: points = self.qdrant_client.scroll( collection_name=self.embedded_collection_name, scroll_filter=qdrant_filter, limit=1, with_payload=True, with_vectors=False, )[0] except Exception: # If filter fails due to missing index, scroll all and filter in memory if qdrant_filter: all_points = self.qdrant_client.scroll( collection_name=self.embedded_collection_name, limit=10000, with_payload=True, with_vectors=False, )[0] # Filter in memory from parlant.core.persistence.common import matches_filters points = [ p for p in all_points if p.payload is not None and matches_filters(filters, p.payload) ][:1] else: points = [] if points: return cast(TDocument, points[0].payload) return None @override async def insert_one( self, document: TDocument, ) -> InsertResult: ensure_is_total(document, self._schema) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[document["content"]], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([document["content"]])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[document["content"]], vectors=embeddings, ) if not embeddings or len(embeddings[0]) == 0: raise ValueError( f"Empty embedding generated for document content: {document['content'][:50]}..." ) async with self._lock.writer_lock: self._version += 1 # Convert string ID to integer for Qdrant point_id = _string_id_to_int(str(document["id"])) # Insert into unembedded collection with retry on timeout await _retry_on_timeout_async( lambda: asyncio.to_thread( self.qdrant_client.upsert, collection_name=self._unembedded_collection_name, points=[ models.PointStruct( id=point_id, vector=[0], payload=cast(dict[str, Any], document), ) ], ), max_retries=3, logger=self._logger, ) # Insert into embedded collection with retry on timeout await _retry_on_timeout_async( lambda: asyncio.to_thread( self.qdrant_client.upsert, collection_name=self.embedded_collection_name, points=[ models.PointStruct( id=point_id, vector=embeddings[0], payload=cast(dict[str, Any], document), ) ], ), max_retries=3, logger=self._logger, ) # Update version in both collections if self._database: await self._database._set_collection_version( self._unembedded_collection_name, self._version ) await self._database._set_collection_version( self.embedded_collection_name, self._version ) return InsertResult(acknowledged=True) @override async def update_one( self, filters: Where, params: TDocument, upsert: bool = False, ) -> UpdateResult[TDocument]: async with self._lock.writer_lock: # Ensure indexes exist for all fields used in filtering if filters and self._database: field_names: set[str] = set() _extract_field_names_from_where(filters, field_names) for field_name in field_names: self._database._ensure_payload_index(self.embedded_collection_name, field_name) qdrant_filter = _convert_where_to_qdrant_filter(filters) points = self.qdrant_client.scroll( collection_name=self.embedded_collection_name, scroll_filter=qdrant_filter, limit=1, with_payload=True, with_vectors=True, )[0] if points: point = points[0] doc = cast(dict[str, Any], point.payload) if "content" in params: content = params["content"] else: content = str(doc["content"]) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[content], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([content])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[content], vectors=embeddings, ) if not embeddings or len(embeddings[0]) == 0: raise ValueError(f"Empty embedding generated for content: {content[:50]}...") updated_document = {**doc, **params} self._version += 1 # Update unembedded collection with retry on timeout await _retry_on_timeout_async( lambda: asyncio.to_thread( self.qdrant_client.upsert, collection_name=self._unembedded_collection_name, points=[ models.PointStruct( id=point.id, # point.id is already an integer vector=[0], payload=updated_document, ) ], ), max_retries=3, logger=self._logger, ) # Update embedded collection with retry on timeout await _retry_on_timeout_async( lambda: asyncio.to_thread( self.qdrant_client.upsert, collection_name=self.embedded_collection_name, points=[ models.PointStruct( id=point.id, # point.id is already an integer vector=embeddings[0], payload=updated_document, ) ], ), max_retries=3, logger=self._logger, ) # Update version in both collections if self._database: await self._database._set_collection_version( self._unembedded_collection_name, self._version ) await self._database._set_collection_version( self.embedded_collection_name, self._version ) return UpdateResult( acknowledged=True, matched_count=1, modified_count=1, updated_document=cast(TDocument, updated_document), ) elif upsert: ensure_is_total(params, self._schema) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[params["content"]], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([params["content"]])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[params["content"]], vectors=embeddings, ) if not embeddings or len(embeddings[0]) == 0: raise ValueError( f"Empty embedding generated for content: {params['content'][:50] if 'content' in params else 'N/A'}..." ) self._version += 1 # Convert string ID to integer for Qdrant point_id = _string_id_to_int(str(params["id"])) # Insert into unembedded collection with retry on timeout await _retry_on_timeout_async( lambda: asyncio.to_thread( self.qdrant_client.upsert, collection_name=self._unembedded_collection_name, points=[ models.PointStruct( id=point_id, vector=[0], payload=cast(dict[str, Any], params), ) ], ), max_retries=3, logger=self._logger, ) # Insert into embedded collection with retry on timeout await _retry_on_timeout_async( lambda: asyncio.to_thread( self.qdrant_client.upsert, collection_name=self.embedded_collection_name, points=[ models.PointStruct( id=point_id, vector=embeddings[0], payload=cast(dict[str, Any], params), ) ], ), max_retries=3, logger=self._logger, ) # Update version in both collections if self._database: await self._database._set_collection_version( self._unembedded_collection_name, self._version ) await self._database._set_collection_version( self.embedded_collection_name, self._version ) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=params, ) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=None, ) @override async def delete_one( self, filters: Where, ) -> DeleteResult[TDocument]: async with self._lock.writer_lock: # Ensure indexes exist for all fields used in filtering if filters and self._database: field_names: set[str] = set() _extract_field_names_from_where(filters, field_names) for field_name in field_names: self._database._ensure_payload_index(self.embedded_collection_name, field_name) qdrant_filter = _convert_where_to_qdrant_filter(filters) points = self.qdrant_client.scroll( collection_name=self.embedded_collection_name, scroll_filter=qdrant_filter, limit=2, # Check for more than one with_payload=True, with_vectors=False, )[0] if len(points) > 1: raise ValueError( f"QdrantCollection delete_one: detected more than one document with filters '{filters}'. Aborting..." ) if points: deleted_document = cast(TDocument, points[0].payload) point_id = points[0].id self._version += 1 # Delete from unembedded collection self.qdrant_client.delete( collection_name=self._unembedded_collection_name, points_selector=models.PointIdsList(points=[point_id]), ) # Delete from embedded collection self.qdrant_client.delete( collection_name=self.embedded_collection_name, points_selector=models.PointIdsList(points=[point_id]), ) # Update version in both collections if self._database: await self._database._set_collection_version( self._unembedded_collection_name, self._version ) await self._database._set_collection_version( self.embedded_collection_name, self._version ) return DeleteResult( deleted_count=1, acknowledged=True, deleted_document=deleted_document, ) return DeleteResult( acknowledged=True, deleted_count=0, deleted_document=None, ) @override async def do_find_similar_documents( self, filters: Where, query: str, k: int, hints: Mapping[str, Any] = {}, ) -> Sequence[SimilarDocumentResult[TDocument]]: async with self._lock.reader_lock: # Ensure indexes exist for all fields used in filtering if filters and self._database: field_names: set[str] = set() _extract_field_names_from_where(filters, field_names) for field_name in field_names: self._database._ensure_payload_index(self.embedded_collection_name, field_name) query_embeddings = list((await self._embedder.embed([query], hints)).vectors) qdrant_filter = _convert_where_to_qdrant_filter(filters) if not query_embeddings or len(query_embeddings[0]) == 0: self._logger.warning(f"Empty embedding generated for query: {query}") return [] search_results = self.qdrant_client.query_points( collection_name=self.embedded_collection_name, query=list(query_embeddings[0]), query_filter=qdrant_filter, limit=k, ).points if not search_results: return [] self._logger.trace( f"Similar documents found\n{json.dumps([r.payload for r in search_results], indent=2)}" ) return [ SimilarDocumentResult( document=cast(TDocument, result.payload), distance=1.0 - result.score, # Convert similarity to distance ) for result in search_results ] ================================================ FILE: src/parlant/adapters/vector_db/transient.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License") # You may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ruff: noqa from __future__ import annotations import asyncio import json from typing import Any, Awaitable, Callable, Generic, Mapping, Optional, Sequence, cast import numpy as np from typing_extensions import override import logging from parlant.core.tracer import Tracer orig_basicConfig = logging.basicConfig orig_getLogger = logging.getLogger # nano_vectordb overrides logging's basicConfig and stuff... :S # So we need to protect it for a minute while importing def _null_basicConfig(*args: Any, **kwargs: Any) -> None: pass class _NullLogger: def info(self, *args: Any, **kwargs: Any) -> None: pass def debug(self, *args: Any, **kwargs: Any) -> None: pass def _null_getLogger(*args: Any, **kwargs: Any) -> object: return _NullLogger() logging.basicConfig = _null_basicConfig # type: ignore logging.getLogger = _null_getLogger # type: ignore import nano_vectordb # type: ignore logging.basicConfig = orig_basicConfig logging.getLogger = orig_getLogger # Back to business from parlant.core.common import JSONSerializable from parlant.core.nlp.embedding import ( Embedder, EmbedderFactory, EmbeddingCache, EmbeddingCacheProvider, ) from parlant.core.loggers import Logger from parlant.core.persistence.common import ensure_is_total, matches_filters, Where from parlant.core.persistence.vector_database import ( BaseDocument, BaseVectorCollection, DeleteResult, InsertResult, SimilarDocumentResult, UpdateResult, VectorDatabase, TDocument, ) class TransientVectorDatabase(VectorDatabase): def __init__( self, logger: Logger, tracer: Tracer, embedder_factory: EmbedderFactory, embedding_cache_provider: EmbeddingCacheProvider, ) -> None: self._logger = logger self._tracer = tracer self._embedder_factory = embedder_factory self._embedding_cache_provider = embedding_cache_provider self._databases: dict[str, nano_vectordb.NanoVectorDB] = {} self._collections: dict[str, TransientVectorCollection[BaseDocument]] = {} self._metadata: dict[str, JSONSerializable] = {} @override async def create_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], ) -> TransientVectorCollection[TDocument]: if name in self._collections: raise ValueError(f'Collection "{name}" already exists.') embedder = self._embedder_factory.create_embedder(embedder_type) self._databases[name] = nano_vectordb.NanoVectorDB(embedder.dimensions) self._collections[name] = TransientVectorCollection( self._logger, self._tracer, nano_db=self._databases[name], name=name, schema=schema, embedder=embedder, embedding_cache_provider=self._embedding_cache_provider, ) return cast(TransientVectorCollection[TDocument], self._collections[name]) @override async def get_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> TransientVectorCollection[TDocument]: if collection := self._collections.get(name): return cast(TransientVectorCollection[TDocument], collection) raise ValueError(f'Transient collection "{name}" not found.') @override async def get_or_create_collection( self, name: str, schema: type[TDocument], embedder_type: type[Embedder], document_loader: Callable[[BaseDocument], Awaitable[Optional[TDocument]]], ) -> TransientVectorCollection[TDocument]: if collection := self._collections.get(name): assert schema == collection._schema return cast(TransientVectorCollection[TDocument], collection) embedder = self._embedder_factory.create_embedder(embedder_type) self._databases[name] = nano_vectordb.NanoVectorDB(embedder.dimensions) self._collections[name] = TransientVectorCollection( self._logger, self._tracer, nano_db=self._databases[name], name=name, schema=schema, embedder=self._embedder_factory.create_embedder(embedder_type), embedding_cache_provider=self._embedding_cache_provider, ) return cast(TransientVectorCollection[TDocument], self._collections[name]) @override async def delete_collection( self, name: str, ) -> None: if name not in self._collections: raise ValueError(f'Collection "{name}" not found.') del self._databases[name] del self._collections[name] @override async def upsert_metadata( self, key: str, value: JSONSerializable, ) -> None: self._metadata[key] = value @override async def remove_metadata( self, key: str, ) -> None: self._metadata.pop(key) @override async def read_metadata( self, ) -> Mapping[str, JSONSerializable]: return self._metadata class TransientVectorCollection(Generic[TDocument], BaseVectorCollection[TDocument]): def __init__( self, logger: Logger, tracer: Tracer, nano_db: nano_vectordb.NanoVectorDB, name: str, schema: type[TDocument], embedder: Embedder, embedding_cache_provider: EmbeddingCacheProvider, ) -> None: self._logger = logger self._tracer = tracer self._name = name self._schema = schema self._embedder = embedder self._embedding_cache_provider = embedding_cache_provider self._lock = asyncio.Lock() self._nano_db = nano_db self._documents: list[TDocument] = [] @staticmethod def _build_filter_lambda( filters: Where, ) -> nano_vectordb.dbs.ConditionLambda: def filter_lambda(candidate: Mapping[str, Any]) -> bool: return matches_filters(filters, candidate) return filter_lambda @override async def find( self, filters: Where, ) -> Sequence[TDocument]: result = [] for doc in filter( lambda d: matches_filters(filters, d), self._documents, ): result.append(doc) return result @override async def find_one( self, filters: Where, ) -> Optional[TDocument]: for doc in self._documents: if matches_filters(filters, doc): return doc return None @override async def insert_one( self, document: TDocument, ) -> InsertResult: ensure_is_total(document, self._schema) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[document["content"]], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([document["content"]])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[document["content"]], vectors=embeddings, ) vector = np.array(embeddings[0], dtype=np.float32) data = {**document, "__id__": document["id"], "__vector__": vector} async with self._lock: self._nano_db.upsert([data]) self._documents.append(document) return InsertResult(acknowledged=True) @override async def update_one( self, filters: Where, params: TDocument, upsert: bool = False, ) -> UpdateResult[TDocument]: async with self._lock: for i, doc in enumerate(self._documents): if matches_filters(filters, doc): if "content" in params: content = params["content"] else: content = str(doc["content"]) if e := await self._embedding_cache_provider().get( embedder_type=type(self._embedder), texts=[content], ): embeddings = list(e.vectors) else: embeddings = list((await self._embedder.embed([content])).vectors) await self._embedding_cache_provider().set( embedder_type=type(self._embedder), texts=[content], vectors=embeddings, ) vector = np.array(embeddings[0], dtype=np.float32) data = {**params, "__id__": doc["id"], "__vector__": vector} self._nano_db.upsert([data]) self._documents[i] = cast(TDocument, {**self._documents[i], **params}) return UpdateResult( acknowledged=True, matched_count=1, modified_count=1, updated_document=self._documents[i], ) if upsert: ensure_is_total(params, self._schema) await self.insert_one(params) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=params, ) return UpdateResult( acknowledged=True, matched_count=0, modified_count=0, updated_document=None, ) @override async def delete_one( self, filters: Where, ) -> DeleteResult[TDocument]: for i, d in enumerate(self._documents): if matches_filters(filters, d): document = self._documents.pop(i) self._nano_db.delete([d["id"]]) return DeleteResult(deleted_count=1, acknowledged=True, deleted_document=document) return DeleteResult( acknowledged=True, deleted_count=0, deleted_document=None, ) @staticmethod def _distance_from_similarity(similarity: float) -> float: return 1 - similarity async def do_find_similar_documents( self, filters: Where, query: str, k: int, hints: Mapping[str, Any] = {}, ) -> Sequence[SimilarDocumentResult[TDocument]]: if not self._documents: return [] query_embeddings = list((await self._embedder.embed([query], hints)).vectors) vector = np.array(query_embeddings[0], dtype=np.float32) keys_to_exclude = {"__id__", "__metrics__"} if await self.find(filters) == []: return [] query_result = self._nano_db.query( query=vector, top_k=len(self._documents), filter_lambda=self._build_filter_lambda(filters), ) docs_and_similarities = [ ( {key: value for key, value in d.items() if key not in keys_to_exclude}, float(d["__metrics__"]), ) for d in query_result ] self._logger.trace( f"Similar documents found\n{json.dumps(docs_and_similarities[0], indent=2)}" ) results = [ SimilarDocumentResult( document=cast(TDocument, d), distance=self._distance_from_similarity(sim), ) for d, sim in docs_and_similarities ] return results ================================================ FILE: src/parlant/api/agents.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from fastapi import APIRouter, Path, Request, status from pydantic import Field from typing import Annotated, Sequence, TypeAlias from parlant.api.authorization import AuthorizationPolicy, Operation from parlant.api.common import ( CompositionModeDTO, ExampleJson, MessageOutputModeDTO, apigen_config, composition_mode_dto_to_composition_mode, composition_mode_to_composition_mode_dto, example_json_content, message_output_mode_dto_to_message_output_mode, message_output_mode_to_message_output_mode_dto, ) from parlant.core.app_modules.agents import AgentTagUpdateParamsModel from parlant.core.agents import AgentId from parlant.core.application import Application from parlant.core.common import DefaultBaseModel from parlant.core.tags import TagId API_GROUP = "agents" AgentIdPath: TypeAlias = Annotated[ AgentId, Path( description="Unique identifier for the agent", examples=["IUCGT-lvpS"], min_length=1, ), ] AgentNameField: TypeAlias = Annotated[ str, Field( description="The display name of the agent, mainly for management purposes", examples=["Haxon", "Alfred J. Quack"], min_length=1, max_length=100, ), ] AgentDescriptionField: TypeAlias = Annotated[ str, Field( description="Detailed description of the agent's purpose and capabilities", examples=["Technical Support Assistant"], ), ] AgentMaxEngineIterationsField: TypeAlias = Annotated[ int, Field( description="Maximum number of processing iterations the agent can perform per request", ge=1, examples=[1, 3], ), ] AgentTagsField: TypeAlias = Annotated[ list[TagId], Field( description="List of tag IDs associated with the agent", examples=[["tag1", "tag2"]], ), ] AgentTagUpdateAddField: TypeAlias = Annotated[ list[TagId], Field( description="List of tag IDs to add to the agent", examples=[["tag1", "tag2"]], ), ] AgentTagUpdateRemoveField: TypeAlias = Annotated[ list[TagId], Field( description="List of tag IDs to remove from the agent", examples=[["tag1", "tag2"]], ), ] agent_example: ExampleJson = { "id": "IUCGT-lvpS", "name": "Haxon", "description": "Technical Support Assistant", "creation_utc": "2024-03-24T12:00:00Z", "max_engine_iterations": 3, "composition_mode": "fluid", "message_output_mode": "block", "tags": ["tag1", "tag2"], } class AgentDTO( DefaultBaseModel, json_schema_extra={"example": agent_example}, ): """ An agent is a specialized AI personality crafted for a specific service role. Agents form the basic unit of conversational customization: all behavioral configurations are made at the agent level. Use this model for representing complete agent information in API responses. """ id: AgentIdPath name: AgentNameField description: AgentDescriptionField | None = None max_engine_iterations: AgentMaxEngineIterationsField = 1 composition_mode: CompositionModeDTO message_output_mode: MessageOutputModeDTO tags: AgentTagsField = [] agent_creation_params_example: ExampleJson = { "name": "Haxon", "description": "Technical Support Assistant", "max_engine_iterations": 3, "composition_mode": "fluid", "message_output_mode": "block", "tags": ["tag1", "tag2"], } class AgentCreationParamsDTO( DefaultBaseModel, json_schema_extra={"example": agent_creation_params_example}, ): """ Parameters for creating a new agent. Optional fields: - `id`: Custom identifier for the agent. If not provided, an ID will be automatically generated. Custom IDs can be any string format and are useful for maintaining consistent identifiers across deployments or integrations. - `description`: Detailed explanation of the agent's purpose - `max_engine_iterations`: Processing limit per request - `composition_mode`: How the agent composes responses - `message_output_mode`: How the agent outputs messages (block or streaming) - `tags`: List of tag IDs to associate with the agent Note: Agents must be created via the API before they can be used. """ name: AgentNameField id: AgentIdPath | None = None description: AgentDescriptionField | None = None max_engine_iterations: AgentMaxEngineIterationsField | None = None composition_mode: CompositionModeDTO | None = None message_output_mode: MessageOutputModeDTO | None = None tags: AgentTagsField | None = None agent_update_params_example: ExampleJson = { "name": "Haxon", "description": "Technical Support Assistant", "max_engine_iterations": 3, "composition_mode": "fluid", "message_output_mode": "block", } tags_update_params_example: ExampleJson = { "add": [ "t9a8g703f4", "tag_456abc", ], "remove": [ "tag_789def", "tag_012ghi", ], } class AgentTagUpdateParamsDTO( DefaultBaseModel, json_schema_extra={"example": tags_update_params_example}, ): """ Parameters for updating an existing agent's tags. """ add: AgentTagUpdateAddField | None = None remove: AgentTagUpdateRemoveField | None = None class AgentUpdateParamsDTO( DefaultBaseModel, json_schema_extra={"example": agent_update_params_example}, ): """ Parameters for updating an existing agent. All fields are optional. only provided fields will be updated. The agent's ID and creation timestamp cannot be modified. """ name: AgentNameField | None = None description: AgentDescriptionField | None = None max_engine_iterations: AgentMaxEngineIterationsField | None = None composition_mode: CompositionModeDTO | None = None message_output_mode: MessageOutputModeDTO | None = None tags: AgentTagUpdateParamsDTO | None = None def create_router( policy: AuthorizationPolicy, app: Application, ) -> APIRouter: router = APIRouter() @router.post( "", status_code=status.HTTP_201_CREATED, operation_id="create_agent", response_model=AgentDTO, responses={ status.HTTP_201_CREATED: { "description": "Agent successfully created. Returns the complete agent object including generated ID.", "content": example_json_content(agent_example), }, status.HTTP_422_UNPROCESSABLE_CONTENT: { "description": "Validation error in request parameters" }, }, **apigen_config(group_name=API_GROUP, method_name="create"), ) async def create_agent( request: Request, params: AgentCreationParamsDTO, ) -> AgentDTO: """ Creates a new agent in the system. The agent will be initialized with the provided name and optional settings. A unique identifier will be automatically generated unless a custom ID is provided. Default behaviors: - `name` defaults to `"Unnamed Agent"` if not provided - `id` is auto-generated if not provided - `description` defaults to `None` - `max_engine_iterations` defaults to `None` (uses system default) """ await policy.authorize( request=request, operation=Operation.CREATE_AGENT, ) agent = await app.agents.create( name=params and params.name or "Unnamed Agent", description=params and params.description or None, max_engine_iterations=params and params.max_engine_iterations or None, composition_mode=composition_mode_dto_to_composition_mode(params.composition_mode) if params and params.composition_mode else None, message_output_mode=message_output_mode_dto_to_message_output_mode( params.message_output_mode ) if params and params.message_output_mode else None, tags=params.tags, id=params.id if params else None, ) return AgentDTO( id=agent.id, name=agent.name, description=agent.description, creation_utc=agent.creation_utc, max_engine_iterations=agent.max_engine_iterations, composition_mode=composition_mode_to_composition_mode_dto(agent.composition_mode), message_output_mode=message_output_mode_to_message_output_mode_dto( agent.message_output_mode ), tags=agent.tags, ) @router.get( "", operation_id="list_agents", response_model=Sequence[AgentDTO], responses={ status.HTTP_200_OK: { "description": "List of all agents in the system", "content": example_json_content([agent_example]), } }, **apigen_config(group_name=API_GROUP, method_name="list"), ) async def list_agents(request: Request) -> Sequence[AgentDTO]: """ Retrieves a list of all agents in the system. Returns an empty list if no agents exist. Agents are returned in no guaranteed order. """ await policy.authorize( request=request, operation=Operation.LIST_AGENTS, ) agents = await app.agents.find() return [ AgentDTO( id=a.id, name=a.name, description=a.description, creation_utc=a.creation_utc, max_engine_iterations=a.max_engine_iterations, composition_mode=composition_mode_to_composition_mode_dto(a.composition_mode), message_output_mode=message_output_mode_to_message_output_mode_dto( a.message_output_mode ), tags=a.tags, ) for a in agents ] @router.get( "/{agent_id}", operation_id="read_agent", response_model=AgentDTO, responses={ status.HTTP_200_OK: { "description": "Agent details successfully retrieved. Returns the complete agent object.", "content": example_json_content(agent_example), }, status.HTTP_404_NOT_FOUND: { "description": "Agent not found. the specified `agent_id` does not exist" }, }, **apigen_config(group_name=API_GROUP, method_name="retrieve"), ) async def read_agent( request: Request, agent_id: AgentIdPath, ) -> AgentDTO: """ Retrieves details of a specific agent by ID. """ await policy.authorize( request=request, operation=Operation.READ_AGENT, ) agent = await app.agents.read(agent_id=agent_id) if await policy.check_permission(request, Operation.READ_AGENT_DESCRIPTION): description = agent.description else: description = None return AgentDTO( id=agent.id, name=agent.name, description=description, creation_utc=agent.creation_utc, max_engine_iterations=agent.max_engine_iterations, composition_mode=composition_mode_to_composition_mode_dto(agent.composition_mode), message_output_mode=message_output_mode_to_message_output_mode_dto( agent.message_output_mode ), tags=agent.tags, ) @router.patch( "/{agent_id}", operation_id="update_agent", response_model=AgentDTO, responses={ status.HTTP_200_OK: { "description": "Agent successfully updated. Returns the updated agent.", "content": example_json_content(agent_example), }, status.HTTP_404_NOT_FOUND: { "description": "Agent not found. the specified `agent_id` does not exist" }, status.HTTP_422_UNPROCESSABLE_CONTENT: { "description": "Validation error in update parameters" }, }, **apigen_config(group_name=API_GROUP, method_name="update"), ) async def update_agent( request: Request, agent_id: AgentIdPath, params: AgentUpdateParamsDTO, ) -> AgentDTO: """ Updates an existing agent's attributes. Only the provided attributes will be updated; others will remain unchanged. The agent's ID and creation timestamp cannot be modified. """ await policy.authorize( request=request, operation=Operation.UPDATE_AGENT, ) agent = await app.agents.update( agent_id=agent_id, name=params.name, description=params.description, max_engine_iterations=params.max_engine_iterations, composition_mode=composition_mode_dto_to_composition_mode(params.composition_mode) if params.composition_mode else None, message_output_mode=message_output_mode_dto_to_message_output_mode( params.message_output_mode ) if params.message_output_mode else None, tags=AgentTagUpdateParamsModel(add=params.tags.add, remove=params.tags.remove) if params.tags else None, ) return AgentDTO( id=agent.id, name=agent.name, description=agent.description, creation_utc=agent.creation_utc, max_engine_iterations=agent.max_engine_iterations, composition_mode=composition_mode_to_composition_mode_dto(agent.composition_mode), message_output_mode=message_output_mode_to_message_output_mode_dto( agent.message_output_mode ), tags=agent.tags, ) @router.delete( "/{agent_id}", operation_id="delete_agent", status_code=status.HTTP_204_NO_CONTENT, responses={ status.HTTP_204_NO_CONTENT: { "description": "Agent successfully deleted. No content returned." }, status.HTTP_404_NOT_FOUND: { "description": "Agent not found. The specified `agent_id` does not exist" }, }, **apigen_config(group_name=API_GROUP, method_name="delete"), ) async def delete_agent( request: Request, agent_id: AgentIdPath, ) -> None: """ Deletes an agent from the agent. Deleting a non-existent agent will return 404. No content will be returned from a successful deletion. """ await policy.authorize( request=request, operation=Operation.DELETE_AGENT, ) await app.agents.delete(agent_id=agent_id) return router ================================================ FILE: src/parlant/api/app.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import asyncio from contextvars import ContextVar import os import traceback from typing import Any, Awaitable, Callable, Mapping, TypeAlias import mimetypes from fastapi import APIRouter, FastAPI, HTTPException, Request, Response, status from fastapi.responses import RedirectResponse from fastapi.routing import APIRoute from fastapi.staticfiles import StaticFiles from starlette.types import Receive, Scope, Send from starlette.routing import Match from lagom import Container from parlant.adapters.loggers.websocket import WebSocketLogger from parlant.api import agents, capabilities from parlant.api import evaluations from parlant.api import journeys from parlant.api import relationships from parlant.api import sessions from parlant.api import glossary from parlant.api import guidelines from parlant.api import context_variables as variables from parlant.api import services from parlant.api import tags from parlant.api import customers from parlant.api import logs from parlant.api import canned_responses from parlant.api.authorization import ( AuthorizationException, AuthorizationPolicy, Operation, RateLimitExceededException, ) from parlant.core.version import VERSION from parlant.core.meter import Meter from parlant.core.tracer import Tracer from parlant.core.common import ItemNotFoundError, generate_id from parlant.core.loggers import Logger from parlant.core.application import Application mimetypes.add_type("text/javascript", ".js") mimetypes.add_type("image/svg+xml", ".svg") ASGIApplication: TypeAlias = Callable[ [ Scope, Receive, Send, ], Awaitable[None], ] class AppWrapper: def __init__(self, app: FastAPI) -> None: self.app = app async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: """FastAPI's built-in exception handling doesn't catch BaseExceptions such as asyncio.CancelledError. This causes the server process to terminate with an ugly traceback. This wrapper addresses that by specifically allowing asyncio.CancelledError to gracefully exit. """ try: return await self.app(scope, receive, send) except asyncio.CancelledError: pass RECORDED_FLAG = "_otel_metrics_recorded" def _resolve_operation_id(request: Request) -> str | None: route = request.scope.get("route") if isinstance(route, APIRoute): return route.operation_id # If scope['route'] not set (404/early errors/etc.), try to match manually for r in getattr(request.app.router, "routes", []): if isinstance(r, APIRoute) and r.matches(request.scope)[0] == Match.FULL: return r.operation_id return None async def create_api_app( container: Container, configure: Callable[[FastAPI], Awaitable[None]] | None = None, contextvar_propagation: Mapping[ContextVar[Any], Any] = {}, ) -> ASGIApplication: logger = container[Logger] websocket_logger = container[WebSocketLogger] tracer = container[Tracer] authorization_policy = container[AuthorizationPolicy] application = container[Application] meter = container[Meter] _hist_http_request_duration = meter.create_duration_histogram( name="httpreq", description="HTTP Request Duration", ) api_app = FastAPI( title="Parlant API", description="API documentation for the Parlant server.", version=VERSION, ) api_app = await authorization_policy.configure_app(api_app) @api_app.middleware("http") async def propagate_contextvars_into_request_task( request: Request, call_next: Callable[[Request], Awaitable[Response]], ) -> Response: for var, value in contextvar_propagation.items(): var.set(value) return await call_next(request) @api_app.middleware("http") async def handle_cancellation( request: Request, call_next: Callable[[Request], Awaitable[Response]], ) -> Response: try: return await call_next(request) except asyncio.CancelledError: return Response(status_code=status.HTTP_503_SERVICE_UNAVAILABLE) @api_app.middleware("http") async def add_trace_id( request: Request, call_next: Callable[[Request], Awaitable[Response]], ) -> Response: if ( request.url.path.startswith("/docs") or request.url.path.startswith("/redoc") or request.url.path.startswith("/openapi.json") ): await authorization_policy.authorize( request=request, operation=Operation.ACCESS_API_DOCS, ) return await call_next(request) if request.url.path.startswith("/chat/"): await authorization_policy.authorize( request=request, operation=Operation.ACCESS_INTEGRATED_UI, ) return await call_next(request) operation_id = _resolve_operation_id(request) if operation_id is None: return await call_next(request) request_id = generate_id() with tracer.span( "http.request", { "http.request.id": request_id, "http.request.operation": operation_id, "http.request.method": request.method, **request.path_params, }, ): async with _hist_http_request_duration.measure( { "http.request.operation": operation_id, "http.request.method": request.method, }, ): return await call_next(request) @api_app.exception_handler(RateLimitExceededException) async def rate_limit_exceeded_handler( request: Request, exc: RateLimitExceededException ) -> HTTPException: logger.trace(f"Rate limit exceeded: {exc}") raise HTTPException( status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail=str(exc), ) @api_app.exception_handler(AuthorizationException) async def authorization_error_handler( request: Request, exc: AuthorizationException ) -> HTTPException: logger.trace(f"Authorization error: {exc}") raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=str(exc), ) @api_app.exception_handler(ItemNotFoundError) async def item_not_found_error_handler( request: Request, exc: ItemNotFoundError ) -> HTTPException: logger.info(str(exc)) raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc), ) @api_app.exception_handler(Exception) async def server_error_handler(request: Request, exc: ItemNotFoundError) -> HTTPException: logger.error(str(exc)) logger.error(str(traceback.format_exception(exc))) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc), ) static_dir = os.path.join(os.path.dirname(__file__), "chat/dist") api_app.mount("/chat", StaticFiles(directory=static_dir, html=True), name="static") @api_app.get("/", include_in_schema=False) async def root() -> Response: return RedirectResponse("/chat") @api_app.get("/healthz") async def health_check() -> dict[str, str]: return {"status": "ok"} agent_router = APIRouter(prefix="/agents") api_app.include_router( router=agents.create_router( policy=authorization_policy, app=application, ), prefix="/agents", ) api_app.include_router( router=agent_router, ) api_app.include_router( prefix="/sessions", router=sessions.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/services", router=services.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/tags", router=tags.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/terms", router=glossary.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/customers", router=customers.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/canned_responses", router=canned_responses.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/context-variables", router=variables.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/guidelines", router=guidelines.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/relationships", router=relationships.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/journeys", router=journeys.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/evaluations", router=evaluations.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( prefix="/capabilities", router=capabilities.create_router( authorization_policy=authorization_policy, app=application, ), ) api_app.include_router( router=logs.create_router( websocket_logger, ) ) # Call configure_api hook if provided if configure: await configure(api_app) # Store FastAPI app in container for access via Server.api property container[FastAPI] = api_app return AppWrapper(api_app) ================================================ FILE: src/parlant/api/authorization.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from abc import ABC, abstractmethod from enum import Enum from typing import Awaitable, Callable from typing_extensions import override from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from limits.storage import MemoryStorage from limits.strategies import ( MovingWindowRateLimiter, FixedWindowRateLimiter, SlidingWindowCounterRateLimiter, ) from limits import RateLimitItem, RateLimitItemPerMinute class Operation(Enum): ACCESS_INTEGRATED_UI = "access_integrated_ui" ACCESS_API_DOCS = "access_api_docs" CREATE_AGENT = "create_agent" READ_AGENT = "read_agent" READ_AGENT_DESCRIPTION = "read_agent_description" LIST_AGENTS = "list_agents" UPDATE_AGENT = "update_agent" DELETE_AGENT = "delete_agent" CREATE_CANNED_RESPONSE = "create_canned_response" READ_CANNED_RESPONSE = "read_canned_response" LIST_CANNED_RESPONSES = "list_canned_responses" UPDATE_CANNED_RESPONSE = "update_canned_response" DELETE_CANNED_RESPONSE = "delete_canned_response" CREATE_CAPABILITY = "create_capability" READ_CAPABILITY = "read_capability" LIST_CAPABILITIES = "list_capabilities" UPDATE_CAPABILITY = "update_capability" DELETE_CAPABILITY = "delete_capability" CREATE_CONTEXT_VARIABLE = "create_context_variable" READ_CONTEXT_VARIABLE = "read_context_variable" LIST_CONTEXT_VARIABLES = "list_context_variables" UPDATE_CONTEXT_VARIABLE = "update_context_variable" DELETE_CONTEXT_VARIABLE = "delete_context_variable" DELETE_CONTEXT_VARIABLES = "delete_context_variables" READ_CONTEXT_VARIABLE_VALUE = "read_context_variable_value" UPDATE_CONTEXT_VARIABLE_VALUE = "update_context_variable_value" DELETE_CONTEXT_VARIABLE_VALUE = "delete_context_variable_value" CREATE_CUSTOMER = "create_customer" READ_CUSTOMER = "read_customer" LIST_CUSTOMERS = "list_customers" UPDATE_CUSTOMER = "update_customer" DELETE_CUSTOMER = "delete_customer" CREATE_EVALUATION = "create_evaluation" READ_EVALUATION = "read_evaluation" CREATE_TERM = "create_term" READ_TERM = "read_term" LIST_TERMS = "list_terms" UPDATE_TERM = "update_term" DELETE_TERM = "delete_term" CREATE_GUIDELINE = "create_guideline" READ_GUIDELINE = "read_guideline" LIST_GUIDELINES = "list_guidelines" UPDATE_GUIDELINE = "update_guideline" DELETE_GUIDELINE = "delete_guideline" CREATE_JOURNEY = "create_journey" READ_JOURNEY = "read_journey" LIST_JOURNEYS = "list_journeys" UPDATE_JOURNEY = "update_journey" DELETE_JOURNEY = "delete_journey" CREATE_RELATIONSHIP = "create_relationship" READ_RELATIONSHIP = "read_relationship" LIST_RELATIONSHIPS = "list_relationships" DELETE_RELATIONSHIP = "delete_relationship" UPDATE_SERVICE = "update_service" READ_SERVICE = "read_service" LIST_SERVICES = "list_services" DELETE_SERVICE = "delete_service" CREATE_GUEST_SESSION = "create_guest_session" CREATE_CUSTOMER_SESSION = "create_customer_session" READ_SESSION = "read_session" LIST_SESSIONS = "list_sessions" UPDATE_SESSION = "update_session" DELETE_SESSION = "delete_session" DELETE_SESSIONS = "delete_sessions" CREATE_CUSTOMER_EVENT = "create_customer_event" CREATE_AGENT_EVENT = "create_agent_event" CREATE_HUMAN_AGENT_EVENT = "create_human_agent_event" CREATE_HUMAN_AGENT_ON_BEHALF_OF_AI_AGENT_EVENT = ( "create_human_agent_on_behalf_of_ai_agent_event" ) OVERRIDE_CUSTOMER_PARTICIPANT = "override_customer_participant" CREATE_STATUS_EVENT = "create_status_event" CREATE_CUSTOM_EVENT = "create_custom_event" LIST_EVENTS = "list_events" READ_EVENT = "read_event" DELETE_EVENTS = "delete_events" UPDATE_EVENT = "update_event" CREATE_TAG = "create_tag" READ_TAG = "read_tag" LIST_TAGS = "list_tags" UPDATE_TAG = "update_tag" DELETE_TAG = "delete_tag" class AuthorizationException(Exception): def __init__( self, request: Request, operation: Operation | None, message_prefix: str = "Authorization failed", ) -> None: super().__init__( f"{message_prefix}: OPERATION={operation.value if operation else 'GENERIC'}, HEADERS={request.headers}" ) self.request = request self.operation = operation class RateLimitExceededException(AuthorizationException): def __init__(self, request: Request, operation: Operation | None) -> None: super().__init__( request=request, operation=operation, message_prefix="Rate limit exceeded", ) class AuthorizationPolicy(ABC): async def configure_app(self, app: FastAPI) -> FastAPI: return app @abstractmethod async def check_permission(self, request: Request, operation: Operation) -> bool: ... @abstractmethod async def check_rate_limit(self, request: Request, operation: Operation) -> bool: ... async def authorize(self, request: Request, operation: Operation) -> None: if not await self.check_permission(request, operation): raise AuthorizationException(request, operation) if not await self.check_rate_limit(request, operation): raise RateLimitExceededException(request, operation) @property @abstractmethod def name(self) -> str: ... class DevelopmentAuthorizationPolicy(AuthorizationPolicy): async def configure_app(self, app: FastAPI) -> FastAPI: # Allow all origins in development app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) return app @override async def check_rate_limit(self, request: Request, operation: Operation) -> bool: # In development, we do not enforce rate limits return True @override async def check_permission(self, request: Request, operation: Operation) -> bool: # In development, we allow all actions return True @property @override def name(self) -> str: return "development" class RateLimiter(ABC): @abstractmethod async def check( self, request: Request, operation: Operation, ) -> bool: ... class ProductionAuthorizationPolicy(AuthorizationPolicy): def __init__(self) -> None: # This can be modified externally to install specific limiters # for specific API operations. self.specific_limiters: dict[ Operation, Callable[[Request, Operation], Awaitable[bool]], ] = {} # It is also possible to change or override the default limiter # for this instance from outside this class (or in subclasses). self.default_limiter: RateLimiter = BasicRateLimiter( rate_limit_item_per_operation={ # Some reasonable defaults... Operation.READ_AGENT: RateLimitItemPerMinute(30), Operation.CREATE_GUEST_SESSION: RateLimitItemPerMinute(10), Operation.READ_SESSION: RateLimitItemPerMinute(30), Operation.LIST_EVENTS: RateLimitItemPerMinute(240), Operation.CREATE_CUSTOMER_EVENT: RateLimitItemPerMinute(30), Operation.CREATE_STATUS_EVENT: RateLimitItemPerMinute(60), } ) async def configure_app(self, app: FastAPI) -> FastAPI: # By default, allow all origins in production as well. # This can be customized in subclasses. # It's recommended to override this method to set more restrictive CORS policies # for your production environment, e.g., by specifying only the origins (site URLs) # from which your application can be accessed safely (e.g., https://your-site.com). app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) return app @property @override def name(self) -> str: return "production" @override async def check_permission(self, request: Request, operation: Operation) -> bool: if operation in [ Operation.READ_AGENT, Operation.CREATE_GUEST_SESSION, Operation.READ_SESSION, Operation.LIST_EVENTS, Operation.CREATE_CUSTOMER_EVENT, ]: return True else: return False @override async def check_rate_limit(self, request: Request, operation: Operation) -> bool: if specific_limiter := self.specific_limiters.get(operation): return await specific_limiter(request, operation) return await self.default_limiter.check(request, operation) class BasicRateLimiter(RateLimiter): def __init__( self, rate_limit_item_per_operation: dict[Operation, RateLimitItem], storage: MemoryStorage | None = None, limiter_type: type[ MovingWindowRateLimiter | FixedWindowRateLimiter | SlidingWindowCounterRateLimiter ] = MovingWindowRateLimiter, ) -> None: self.rate_limit_item_per_operation = rate_limit_item_per_operation self._limiter = limiter_type(storage or MemoryStorage()) self._default_rate_limit_item = RateLimitItemPerMinute(100) async def check( self, request: Request, operation: Operation, ) -> bool: if item := self.rate_limit_item_per_operation.get(operation): return self._limiter.hit(item, self._build_key(request, operation)) return self._limiter.hit(self._default_rate_limit_item, self._build_key(request, None)) def _build_key( self, request: Request, operation: Operation | None, ) -> str: ip = self._get_client_ip(request) if not ip: raise AuthorizationException( request=request, operation=operation, message_prefix="Authorization failed: No client IP found", ) return f"IP={ip}--OP={operation.value if operation else 'GENERIC'}" @staticmethod def _get_client_ip(request: Request) -> str | None: headers = request.headers if xff := headers.get("x-forwarded-for"): return xff.split(",")[0].strip() if xri := headers.get("x-real-ip"): return xri.strip() if cf := headers.get("cf-connecting-ip"): return cf.strip() return request.client.host if request.client else None ================================================ FILE: src/parlant/api/canned_responses.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from datetime import datetime from typing import Annotated, Sequence, TypeAlias import dateutil from fastapi import APIRouter, HTTPException, Query, Request, status from pydantic import Field from parlant.api.authorization import AuthorizationPolicy, Operation from parlant.core.app_modules.canned_responses import ( CannedResponseTagUpdateParamsModel, CannedResponseMetadataUpdateParamsModel, ) from parlant.core.application import Application from parlant.core.common import DefaultBaseModel from parlant.core.canned_responses import ( CannedResponseId, CannedResponseField, ) from parlant.core.tags import TagId from parlant.api.common import ExampleJson, JSONSerializableDTO, apigen_config, example_json_content API_GROUP = "canned_responses" CannedResponseFieldNameField: TypeAlias = Annotated[ str, Field( description="The name of the canned response field.", examples=["username", "location"], min_length=1, ), ] CannedResponseFieldDescriptionField: TypeAlias = Annotated[ str, Field( description="A description of the canned response field.", examples=["User's name", "Geographical location"], min_length=0, ), ] CannedResponseFieldExampleField: TypeAlias = Annotated[ str, Field( description="An example value for the canned response field.", examples=["Alice", "New York"], min_length=0, ), ] canned_response_field_example: ExampleJson = { "description": "An example value for the canned response field.", "examples": ["Alice", "New York"], "min_length": 1, } class CannedResponseFieldDTO( DefaultBaseModel, json_schema_extra={"example": canned_response_field_example}, ): name: CannedResponseFieldNameField description: CannedResponseFieldDescriptionField examples: list[CannedResponseFieldExampleField] CannedResponseFieldSequenceField: TypeAlias = Annotated[ Sequence[CannedResponseFieldDTO], Field( description="A sequence of canned response fields associated with the canned response.", examples=[ [{"name": "username", "description": "User's name", "examples": ["Alice", "Bob"]}] ], ), ] TagIdField: TypeAlias = Annotated[ TagId, Field( description="Unique identifier for the tag", examples=["t9a8g703f4"], ), ] TagIdSequenceField: TypeAlias = Annotated[ Sequence[TagIdField], Field( description="Collection of tag IDs associated with the canned response.", examples=[["tag123", "tag456"], []], ), ] CannedResponseSignalSequenceField: TypeAlias = Annotated[ Sequence[str], Field( description="A sequence of signals associated with the canned response, to help with filtering and matching.", examples=[ ["What is your name?", "Where are you located?", "Let me know if I can help you."], ], ), ] CannedResponseFieldDependenciesField: TypeAlias = Annotated[ Sequence[str], Field( description="A sequence of field names that must be available in context for this response to be considered.", examples=[ ["order", "customer"], ], ), ] CannedResponseMetadataField: TypeAlias = Annotated[ dict[str, JSONSerializableDTO], Field( description="Additional metadata associated with the canned response.", examples=[{"category": "greeting", "priority": 1}], ), ] CannedResponseMetadataUnsetField: TypeAlias = Annotated[ Sequence[str], Field( description="Metadata keys to remove from the canned response", examples=[["old_key", "deprecated_field"]], ), ] CannedResponseIdField: TypeAlias = Annotated[ CannedResponseId, Field( description="Unique identifier for the tag", examples=["t9a8g703f4"], ), ] CannedResponseCreationUTCField: TypeAlias = Annotated[ datetime, Field( description="UTC timestamp of when the canned response was created", examples=[dateutil.parser.parse("2024-03-24T12:00:00Z")], ), ] CannedResponseValueField: TypeAlias = Annotated[ str, Field( description="The textual content of the canned response.", examples=["Your account balance is {balance}", "the answer is {answer}"], min_length=1, ), ] canned_response_example: ExampleJson = { "id": "frag123", "creation_utc": "2024-03-24T12:00:00Z", "value": "Your account balance is {balance}", "fields": [{"name": "balance", "description": "Account's balance", "examples": [9000]}], "tags": ["private", "office"], "signals": ["What is your balance?", "How much money do I have?"], "metadata": {"category": "account", "priority": 1}, "field_dependencies": ["account"], } class CannedResponseDTO( DefaultBaseModel, json_schema_extra={"example": canned_response_example}, ): id: CannedResponseIdField creation_utc: CannedResponseCreationUTCField value: CannedResponseValueField fields: CannedResponseFieldSequenceField tags: TagIdSequenceField signals: CannedResponseSignalSequenceField metadata: CannedResponseMetadataField field_dependencies: CannedResponseFieldDependenciesField = [] canned_response_creation_params_example: ExampleJson = { "value": "Your account balance is {balance}", "fields": [ { "name": "balance", "description": "Account's balance", "examples": ["9000"], } ], "metadata": {"category": "account", "priority": 1}, "field_dependencies": ["account"], } class CannedResponseCreationParamsDTO( DefaultBaseModel, json_schema_extra={"example": canned_response_creation_params_example}, ): """Parameters for creating a new canned response.""" value: CannedResponseValueField fields: CannedResponseFieldSequenceField tags: TagIdSequenceField | None = None signals: CannedResponseSignalSequenceField | None = None metadata: CannedResponseMetadataField | None = None field_dependencies: CannedResponseFieldDependenciesField | None = None CannedResponseTagUpdateAddField: TypeAlias = Annotated[ Sequence[TagIdField], Field( description="Optional collection of tag ids to add to the canned response's tags", ), ] CannedResponseTagUpdateRemoveField: TypeAlias = Annotated[ Sequence[TagIdField], Field( description="Optional collection of tag ids to remove from the canned response's tags", ), ] tags_update_params_example: ExampleJson = { "add": [ "t9a8g703f4", "tag_456abc", ], "remove": [ "tag_789def", "tag_012ghi", ], } class CannedResponseTagUpdateParamsDTO( DefaultBaseModel, json_schema_extra={"example": tags_update_params_example}, ): """ Parameters for updating a canned response's tags. Allows adding new tags to and removing existing tags from a canned response. Both operations can be performed in a single request. """ add: CannedResponseTagUpdateAddField | None = None remove: CannedResponseTagUpdateRemoveField | None = None canned_response_metadata_update_params_example: ExampleJson = { "set": { "category": "account", "priority": 2, "version": "1.1", }, "unset": ["old_category", "deprecated_field"], } class CannedResponseMetadataUpdateParamsDTO( DefaultBaseModel, json_schema_extra={"example": canned_response_metadata_update_params_example}, ): """Parameters for updating the metadata of a canned response.""" set: CannedResponseMetadataField | None = None unset: CannedResponseMetadataUnsetField | None = None canned_response_update_params_example: ExampleJson = { "value": "Your updated balance is {balance}", "fields": [ { "name": "balance", "description": "Updated account balance", "examples": ["10000"], }, ], "metadata": { "set": { "category": "account", "priority": 2, }, "unset": ["old_field"], }, } class CannedResponseUpdateParamsDTO( DefaultBaseModel, json_schema_extra={"example": canned_response_update_params_example}, ): """Parameters for updating an existing canned response.""" value: CannedResponseValueField | None = None fields: CannedResponseFieldSequenceField | None = None tags: CannedResponseTagUpdateParamsDTO | None = None metadata: CannedResponseMetadataUpdateParamsDTO | None = None def _dto_to_canned_response_field(dto: CannedResponseFieldDTO) -> CannedResponseField: return CannedResponseField( name=dto.name, description=dto.description, examples=dto.examples, ) def _canned_response_field_to_dto( canned_response_field: CannedResponseField, ) -> CannedResponseFieldDTO: return CannedResponseFieldDTO( name=canned_response_field.name, description=canned_response_field.description, examples=canned_response_field.examples, ) TagsQuery: TypeAlias = Annotated[ Sequence[TagId], Query(description="Filter canned responses by tags", examples=["tag1", "tag2"]), ] def create_router( authorization_policy: AuthorizationPolicy, app: Application, ) -> APIRouter: router = APIRouter() @router.post( "", operation_id="create_canned_response", status_code=status.HTTP_201_CREATED, response_model=CannedResponseDTO, responses={ status.HTTP_201_CREATED: { "description": "CannedResponse successfully created.", "content": example_json_content(canned_response_example), }, }, **apigen_config(group_name=API_GROUP, method_name="create"), ) async def create_canned_response( request: Request, params: CannedResponseCreationParamsDTO, ) -> CannedResponseDTO: await authorization_policy.authorize(request, Operation.CREATE_CANNED_RESPONSE) canrep = await app.canned_responses.create( value=params.value, fields=[_dto_to_canned_response_field(s) for s in params.fields], tags=params.tags or None, signals=params.signals or None, metadata=params.metadata or {}, field_dependencies=params.field_dependencies or None, ) return CannedResponseDTO( id=canrep.id, creation_utc=canrep.creation_utc, value=canrep.value, fields=[_canned_response_field_to_dto(s) for s in canrep.fields], tags=canrep.tags, signals=canrep.signals, metadata=canrep.metadata, field_dependencies=canrep.field_dependencies, ) @router.get( "/{canned_response_id}", operation_id="read_canned_response", response_model=CannedResponseDTO, responses={ status.HTTP_200_OK: { "description": "Canned response details successfully retrieved. Returns the CannedResponse object.", "content": example_json_content(canned_response_example), }, status.HTTP_404_NOT_FOUND: { "description": "Canned response not found. The specified canned_response_id does not exist" }, }, **apigen_config(group_name=API_GROUP, method_name="retrieve"), ) async def read_canned_response( request: Request, canned_response_id: CannedResponseIdField, ) -> CannedResponseDTO: """Retrieves details of a specific canned response by ID.""" await authorization_policy.authorize(request, Operation.READ_CANNED_RESPONSE) canrep = await app.canned_responses.read(canned_response_id=canned_response_id) return CannedResponseDTO( id=canrep.id, creation_utc=canrep.creation_utc, value=canrep.value, fields=[_canned_response_field_to_dto(s) for s in canrep.fields], tags=canrep.tags, signals=canrep.signals, metadata=canrep.metadata, field_dependencies=canrep.field_dependencies, ) @router.get( "", operation_id="list_canned_responses", response_model=Sequence[CannedResponseDTO], responses={ status.HTTP_200_OK: { "description": "List of all canned responses in the system", "content": example_json_content([canned_response_example]), } }, **apigen_config(group_name=API_GROUP, method_name="list"), ) async def list_canned_responses( request: Request, tags: TagsQuery = [] ) -> Sequence[CannedResponseDTO]: """Lists all canned responses, optionally filtered by tags.""" await authorization_policy.authorize(request, Operation.LIST_CANNED_RESPONSES) canreps = await app.canned_responses.find(tags=tags) return [ CannedResponseDTO( id=f.id, creation_utc=f.creation_utc, value=f.value, fields=[_canned_response_field_to_dto(s) for s in f.fields], tags=f.tags, signals=f.signals, metadata=f.metadata, field_dependencies=f.field_dependencies, ) for f in canreps ] @router.patch( "/{canned_response_id}", operation_id="update_canned_response", response_model=CannedResponseDTO, responses={ status.HTTP_200_OK: { "description": "Canned response successfully updated. Returns the updated CannedResponse object.", "content": example_json_content(canned_response_example), }, status.HTTP_404_NOT_FOUND: { "description": "CannedResponse not found. The specified canned_response_id does not exist" }, status.HTTP_422_UNPROCESSABLE_CONTENT: { "description": "Validation error in update parameters" }, }, **apigen_config(group_name=API_GROUP, method_name="update"), ) async def update_canned_response( request: Request, canned_response_id: CannedResponseIdField, params: CannedResponseUpdateParamsDTO, ) -> CannedResponseDTO: """ Updates an existing canned response's attributes. Only provided attributes will be updated; others remain unchanged. The canned response's ID and creation timestamp cannot be modified. Extra metadata and tags can be added or removed independently. """ await authorization_policy.authorize(request, Operation.UPDATE_CANNED_RESPONSE) if params.fields and not params.value: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail="CannedResponse fields cannot be updated without providing a new value.", ) metadata_params = None if params.metadata: metadata_params = CannedResponseMetadataUpdateParamsModel( set=params.metadata.set, unset=params.metadata.unset, ) canrep = await app.canned_responses.update( canned_response_id=canned_response_id, value=params.value, fields=( [_dto_to_canned_response_field(s) for s in params.fields] if params.fields else [] ), tags=CannedResponseTagUpdateParamsModel(add=params.tags.add, remove=params.tags.remove) if params.tags else None, metadata=metadata_params, ) return CannedResponseDTO( id=canrep.id, creation_utc=canrep.creation_utc, value=canrep.value, fields=[_canned_response_field_to_dto(s) for s in canrep.fields], tags=canrep.tags, signals=canrep.signals, metadata=canrep.metadata, field_dependencies=canrep.field_dependencies, ) @router.delete( "/{canned_response_id}", operation_id="delete_canned_response", status_code=status.HTTP_204_NO_CONTENT, responses={ status.HTTP_204_NO_CONTENT: { "description": "CannedResponse successfully deleted. No content returned." }, status.HTTP_404_NOT_FOUND: { "description": "CannedResponse not found. The specified canned_response_id does not exist" }, }, **apigen_config(group_name=API_GROUP, method_name="delete"), ) async def delete_canned_response( request: Request, canned_response_id: CannedResponseIdField ) -> None: await authorization_policy.authorize(request, Operation.DELETE_CANNED_RESPONSE) await app.canned_responses.delete(canned_response_id) return router ================================================ FILE: src/parlant/api/capabilities.py ================================================ # Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from fastapi import APIRouter, Path, Query, Request, status from pydantic import Field from typing import Annotated, Sequence, TypeAlias from parlant.api.authorization import AuthorizationPolicy, Operation from parlant.core.app_modules.capabilities import CapabilityTagUpdateParamsModel from parlant.core.application import Application from parlant.core.common import DefaultBaseModel from parlant.api.common import ExampleJson, apigen_config, example_json_content from parlant.core.capabilities import CapabilityId from parlant.core.tags import TagId API_GROUP = "capabilities" CapabilityIdPath: TypeAlias = Annotated[ CapabilityId, Path( description="Unique identifier for the capability", examples=["cap_123abc"], min_length=1, ), ] CapabilityTitleField: TypeAlias = Annotated[ str, Field( description="The title of the capability", examples=["Reset password", "Replace phone"], min_length=1, max_length=100, ), ] CapabilityDescriptionField: TypeAlias = Annotated[ str, Field( description="Detailed description of the capability's purpose", examples=["Provide a weather update"], ), ] CapabilitySignalsField: TypeAlias = Annotated[ Sequence[str], Field( description="Example signals that this capability can handle", examples=[["I thought I remembered my password", "My phone just broke"]], ), ] CapabilityTagsField: TypeAlias = Annotated[ list[TagId], Field( description="List of tag IDs associated with the capability", examples=[["tag1", "tag2"]], ), ] capability_example: ExampleJson = { "id": "cap_123abc", "title": "Provide Replacement Phone", "description": "Provide a replacement phone when a customer needs repair for their phone.", "signals": ["My phone is broken", "I need a replacement while my phone is being repaired"], "tags": ["tag1", "tag2"], } class CapabilityDTO( DefaultBaseModel, json_schema_extra={"example": capability_example}, ): """ A capability represents a functional feature or skill of the agent. """ id: CapabilityIdPath title: CapabilityTitleField description: CapabilityDescriptionField signals: CapabilitySignalsField tags: CapabilityTagsField = [] class CapabilityCreationParamsDTO( DefaultBaseModel, json_schema_extra={"example": capability_example}, ): """ Parameters for creating a new capability. """ title: CapabilityTitleField description: CapabilityDescriptionField signals: CapabilitySignalsField tags: CapabilityTagsField | None = None CapabilityTagUpdateAddField: TypeAlias = Annotated[ list[TagId], Field( description="List of tag IDs to add to the capability", examples=[["tag1", "tag2"]], ), ] CapabilityTagUpdateRemoveField: TypeAlias = Annotated[ list[TagId], Field( description="List of tag IDs to remove from the capability", examples=[["tag1", "tag2"]], ), ] capability_tag_update_params_example: ExampleJson = { "add": ["tag1", "tag2"], "remove": ["tag3"], } class CapabilityTagUpdateParamsDTO( DefaultBaseModel, json_schema_extra={"example": capability_tag_update_params_example}, ): """ Parameters for updating an existing capability's tags. """ add: CapabilityTagUpdateAddField | None = None remove: CapabilityTagUpdateRemoveField | None = None class CapabilityUpdateParamsDTO( DefaultBaseModel, json_schema_extra={"example": capability_example}, ): """ Parameters for updating an existing capability. All fields are optional. Only provided fields will be updated. """ title: CapabilityTitleField | None = None description: CapabilityDescriptionField | None = None signals: CapabilitySignalsField | None = None tags: CapabilityTagUpdateParamsDTO | None = None TagIdQuery: TypeAlias = Annotated[ TagId | None, Query( description="The tag ID to filter capabilities by", examples=["tag:123"], ), ] def create_router( authorization_policy: AuthorizationPolicy, app: Application, ) -> APIRouter: router = APIRouter() @router.post( "", status_code=status.HTTP_201_CREATED, operation_id="create_capability", response_model=CapabilityDTO, responses={ status.HTTP_201_CREATED: { "description": "Capability successfully created. Returns the complete capability object including generated ID.", "content": example_json_content(capability_example), }, status.HTTP_422_UNPROCESSABLE_CONTENT: { "description": "Validation error in request parameters" }, }, **apigen_config(group_name=API_GROUP, method_name="create"), ) async def create_capability( request: Request, params: CapabilityCreationParamsDTO, ) -> CapabilityDTO: """ Creates a new capability in the system. The capability will be initialized with the provided title, description, signals, and optional tags. A unique identifier will be automatically generated. Default behaviors: - `signals` defaults to an empty list if not provided """ await authorization_policy.authorize(request, Operation.CREATE_CAPABILITY) capability = await app.capabilities.create( params.title, params.description, params.signals, params.tags ) return CapabilityDTO( id=capability.id, title=capability.title, description=capability.description, signals=capability.signals, tags=capability.tags, ) @router.get( "", operation_id="list_capabilities", response_model=Sequence[CapabilityDTO], responses={ status.HTTP_200_OK: { "description": "List of all capabilities in the system", "content": example_json_content([capability_example]), } }, **apigen_config(group_name=API_GROUP, method_name="list"), ) async def list_capabilities( request: Request, tag_id: TagIdQuery = None, ) -> Sequence[CapabilityDTO]: """ Retrieves a list of all capabilities in the system. Returns an empty list if no capabilities exist. Capabilities are returned in no guaranteed order. """ await authorization_policy.authorize(request, Operation.LIST_CAPABILITIES) capabilities = await app.capabilities.find(tag_id) return [ CapabilityDTO( id=capability.id, title=capability.title, description=capability.description, signals=capability.signals, tags=capability.tags, ) for capability in capabilities ] @router.get( "/{capability_id}", operation_id="read_capability", response_model=CapabilityDTO, responses={ status.HTTP_200_OK: { "description": "Capability details successfully retrieved. Returns the complete capability object.", "content": example_json_content(capability_example), }, status.HTTP_404_NOT_FOUND: { "description": "Capability not found. The specified `capability_id` does not exist" }, }, **apigen_config(group_name=API_GROUP, method_name="retrieve"), ) async def read_capability( request: Request, capability_id: CapabilityIdPath, ) -> CapabilityDTO: """ Retrieves details of a specific capability by ID. Returns the complete capability object. """ await authorization_policy.authorize(request, Operation.READ_CAPABILITY) capability = await app.capabilities.read(capability_id=capability_id) return CapabilityDTO( id=capability.id, title=capability.title, description=capability.description, signals=capability.signals, tags=capability.tags, ) @router.patch( "/{capability_id}", operation_id="update_capability", response_model=CapabilityDTO, responses={ status.HTTP_200_OK: { "description": "Capability successfully updated. Returns the updated capability.", "content": example_json_content(capability_example), }, status.HTTP_404_NOT_FOUND: { "description": "Capability not found. The specified `capability_id` does not exist" }, status.HTTP_422_UNPROCESSABLE_CONTENT: { "description": "Validation error in update parameters" }, }, **apigen_config(group_name=API_GROUP, method_name="update"), ) async def update_capability( request: Request, capability_id: CapabilityIdPath, params: CapabilityUpdateParamsDTO, ) -> CapabilityDTO: """ Updates an existing capability's attributes. Only the provided attributes will be updated; others will remain unchanged. The capability's ID and creation timestamp cannot be modified. """ await authorization_policy.authorize(request, Operation.UPDATE_CAPABILITY) capability = await app.capabilities.update( capability_id, title=params.title, description=params.description, signals=params.signals, tags=CapabilityTagUpdateParamsModel( add=params.tags.add, remove=params.tags.remove, ) if params.tags else None, ) return CapabilityDTO( id=capability.id, title=capability.title, description=capability.description, signals=capability.signals, tags=capability.tags, ) @router.delete( "/{capability_id}", operation_id="delete_capability", status_code=status.HTTP_204_NO_CONTENT, responses={ status.HTTP_204_NO_CONTENT: { "description": "Capability successfully deleted. No content returned." }, status.HTTP_404_NOT_FOUND: { "description": "Capability not found. The specified `capability_id` does not exist" }, }, **apigen_config(group_name=API_GROUP, method_name="delete"), ) async def delete_capability( request: Request, capability_id: CapabilityIdPath, ) -> None: """ Deletes a capability from the system. Deleting a non-existent capability will return 404. No content will be returned from a successful deletion. """ await authorization_policy.authorize(request, Operation.DELETE_CAPABILITY) await app.capabilities.delete(capability_id=capability_id) return router ================================================ FILE: src/parlant/api/chat/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* node_modules dist-ssr *.local # Editor directories and files .vscode/* !.vscode/extensions.json .idea .DS_Store *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: src/parlant/api/chat/.prettierrc ================================================ { "singleQuote": true, "tabWidth": 2, "semi": true, "bracketSameLine": true, "arrowParens": "always", "bracketSpacing": false, "jsxSingleQuote": true, "printWidth": 250, "useTabs": true } ================================================ FILE: src/parlant/api/chat/.vite/deps_temp_0491001f/package.json ================================================ { "type": "module" } ================================================ FILE: src/parlant/api/chat/components.json ================================================ { "$schema": "https://ui.shadcn.com/schema.json", "style": "default", "rsc": false, "tsx": true, "tailwind": { "config": "tailwind.config.js", "css": "src/index.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" }, "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib", "hooks": "@/hooks" } } ================================================ FILE: src/parlant/api/chat/dist/assets/index-BBAJ1vle.js ================================================ function DM(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();function _s(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var lg={exports:{}},wu={},ug={exports:{}},Et={};/** * @license React * react.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var nv;function LM(){if(nv)return Et;nv=1;var t=Symbol.for("react.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.provider"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),f=Symbol.for("react.lazy"),p=Symbol.iterator;function m(U){return U===null||typeof U!="object"?null:(U=p&&U[p]||U["@@iterator"],typeof U=="function"?U:null)}var g={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},x=Object.assign,v={};function S(U,Q,R){this.props=U,this.context=Q,this.refs=v,this.updater=R||g}S.prototype.isReactComponent={},S.prototype.setState=function(U,Q){if(typeof U!="object"&&typeof U!="function"&&U!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,U,Q,"setState")},S.prototype.forceUpdate=function(U){this.updater.enqueueForceUpdate(this,U,"forceUpdate")};function C(){}C.prototype=S.prototype;function A(U,Q,R){this.props=U,this.context=Q,this.refs=v,this.updater=R||g}var k=A.prototype=new C;k.constructor=A,x(k,S.prototype),k.isPureReactComponent=!0;var M=Array.isArray,F=Object.prototype.hasOwnProperty,I={current:null},D={key:!0,ref:!0,__self:!0,__source:!0};function G(U,Q,R){var oe,pe={},ue=null,J=null;if(Q!=null)for(oe in Q.ref!==void 0&&(J=Q.ref),Q.key!==void 0&&(ue=""+Q.key),Q)F.call(Q,oe)&&!D.hasOwnProperty(oe)&&(pe[oe]=Q[oe]);var he=arguments.length-2;if(he===1)pe.children=R;else if(1>>1,Q=j[U];if(0>>1;Ui(pe,O))uei(J,pe)?(j[U]=J,j[ue]=O,U=ue):(j[U]=pe,j[oe]=O,U=oe);else if(uei(J,O))j[U]=J,j[ue]=O,U=ue;else break e}}return W}function i(j,W){var O=j.sortIndex-W.sortIndex;return O!==0?O:j.id-W.id}if(typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],d=[],f=1,p=null,m=3,g=!1,x=!1,v=!1,S=typeof setTimeout=="function"?setTimeout:null,C=typeof clearTimeout=="function"?clearTimeout:null,A=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function k(j){for(var W=n(d);W!==null;){if(W.callback===null)r(d);else if(W.startTime<=j)r(d),W.sortIndex=W.expirationTime,e(c,W);else break;W=n(d)}}function M(j){if(v=!1,k(j),!x)if(n(c)!==null)x=!0,ae(F);else{var W=n(d);W!==null&&de(M,W.startTime-j)}}function F(j,W){x=!1,v&&(v=!1,C(G),G=-1),g=!0;var O=m;try{for(k(W),p=n(c);p!==null&&(!(p.expirationTime>W)||j&&!Y());){var U=p.callback;if(typeof U=="function"){p.callback=null,m=p.priorityLevel;var Q=U(p.expirationTime<=W);W=t.unstable_now(),typeof Q=="function"?p.callback=Q:p===n(c)&&r(c),k(W)}else r(c);p=n(c)}if(p!==null)var R=!0;else{var oe=n(d);oe!==null&&de(M,oe.startTime-W),R=!1}return R}finally{p=null,m=O,g=!1}}var I=!1,D=null,G=-1,X=5,P=-1;function Y(){return!(t.unstable_now()-Pj||125U?(j.sortIndex=O,e(d,j),n(c)===null&&j===n(d)&&(v?(C(G),G=-1):v=!0,de(M,O-U))):(j.sortIndex=Q,e(c,j),x||g||(x=!0,ae(F))),j},t.unstable_shouldYield=Y,t.unstable_wrapCallback=function(j){var W=m;return function(){var O=m;m=W;try{return j.apply(this,arguments)}finally{m=O}}}})(fg)),fg}var av;function UM(){return av||(av=1,dg.exports=BM()),dg.exports}/** * @license React * react-dom.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var lv;function HM(){if(lv)return Cr;lv=1;var t=Dh(),e=UM();function n(a){for(var u="https://reactjs.org/docs/error-decoder.html?invariant="+a,h=1;h"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),c=Object.prototype.hasOwnProperty,d=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,f={},p={};function m(a){return c.call(p,a)?!0:c.call(f,a)?!1:d.test(a)?p[a]=!0:(f[a]=!0,!1)}function g(a,u,h,b){if(h!==null&&h.type===0)return!1;switch(typeof u){case"function":case"symbol":return!0;case"boolean":return b?!1:h!==null?!h.acceptsBooleans:(a=a.toLowerCase().slice(0,5),a!=="data-"&&a!=="aria-");default:return!1}}function x(a,u,h,b){if(u===null||typeof u>"u"||g(a,u,h,b))return!0;if(b)return!1;if(h!==null)switch(h.type){case 3:return!u;case 4:return u===!1;case 5:return isNaN(u);case 6:return isNaN(u)||1>u}return!1}function v(a,u,h,b,y,_,N){this.acceptsBooleans=u===2||u===3||u===4,this.attributeName=b,this.attributeNamespace=y,this.mustUseProperty=h,this.propertyName=a,this.type=u,this.sanitizeURL=_,this.removeEmptyString=N}var S={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){S[a]=new v(a,0,!1,a,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var u=a[0];S[u]=new v(u,1,!1,a[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(a){S[a]=new v(a,2,!1,a.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){S[a]=new v(a,2,!1,a,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){S[a]=new v(a,3,!1,a.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(a){S[a]=new v(a,3,!0,a,null,!1,!1)}),["capture","download"].forEach(function(a){S[a]=new v(a,4,!1,a,null,!1,!1)}),["cols","rows","size","span"].forEach(function(a){S[a]=new v(a,6,!1,a,null,!1,!1)}),["rowSpan","start"].forEach(function(a){S[a]=new v(a,5,!1,a.toLowerCase(),null,!1,!1)});var C=/[\-:]([a-z])/g;function A(a){return a[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var u=a.replace(C,A);S[u]=new v(u,1,!1,a,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var u=a.replace(C,A);S[u]=new v(u,1,!1,a,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(a){var u=a.replace(C,A);S[u]=new v(u,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(a){S[a]=new v(a,1,!1,a.toLowerCase(),null,!1,!1)}),S.xlinkHref=new v("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(a){S[a]=new v(a,1,!1,a.toLowerCase(),null,!0,!0)});function k(a,u,h,b){var y=S.hasOwnProperty(u)?S[u]:null;(y!==null?y.type!==0:b||!(2H||y[N]!==_[H]){var K=` `+y[N].replace(" at new "," at ");return a.displayName&&K.includes("")&&(K=K.replace("",a.displayName)),K}while(1<=N&&0<=H);break}}}finally{R=!1,Error.prepareStackTrace=h}return(a=a?a.displayName||a.name:"")?Q(a):""}function pe(a){switch(a.tag){case 5:return Q(a.type);case 16:return Q("Lazy");case 13:return Q("Suspense");case 19:return Q("SuspenseList");case 0:case 2:case 15:return a=oe(a.type,!1),a;case 11:return a=oe(a.type.render,!1),a;case 1:return a=oe(a.type,!0),a;default:return""}}function ue(a){if(a==null)return null;if(typeof a=="function")return a.displayName||a.name||null;if(typeof a=="string")return a;switch(a){case D:return"Fragment";case I:return"Portal";case X:return"Profiler";case G:return"StrictMode";case ie:return"Suspense";case Z:return"SuspenseList"}if(typeof a=="object")switch(a.$$typeof){case Y:return(a.displayName||"Context")+".Consumer";case P:return(a._context.displayName||"Context")+".Provider";case z:var u=a.render;return a=a.displayName,a||(a=u.displayName||u.name||"",a=a!==""?"ForwardRef("+a+")":"ForwardRef"),a;case ee:return u=a.displayName||null,u!==null?u:ue(a.type)||"Memo";case ae:u=a._payload,a=a._init;try{return ue(a(u))}catch{}}return null}function J(a){var u=a.type;switch(a.tag){case 24:return"Cache";case 9:return(u.displayName||"Context")+".Consumer";case 10:return(u._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=u.render,a=a.displayName||a.name||"",u.displayName||(a!==""?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return u;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ue(u);case 8:return u===G?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof u=="function")return u.displayName||u.name||null;if(typeof u=="string")return u}return null}function he(a){switch(typeof a){case"boolean":case"number":case"string":case"undefined":return a;case"object":return a;default:return""}}function _e(a){var u=a.type;return(a=a.nodeName)&&a.toLowerCase()==="input"&&(u==="checkbox"||u==="radio")}function ke(a){var u=_e(a)?"checked":"value",h=Object.getOwnPropertyDescriptor(a.constructor.prototype,u),b=""+a[u];if(!a.hasOwnProperty(u)&&typeof h<"u"&&typeof h.get=="function"&&typeof h.set=="function"){var y=h.get,_=h.set;return Object.defineProperty(a,u,{configurable:!0,get:function(){return y.call(this)},set:function(N){b=""+N,_.call(this,N)}}),Object.defineProperty(a,u,{enumerable:h.enumerable}),{getValue:function(){return b},setValue:function(N){b=""+N},stopTracking:function(){a._valueTracker=null,delete a[u]}}}}function Ve(a){a._valueTracker||(a._valueTracker=ke(a))}function ot(a){if(!a)return!1;var u=a._valueTracker;if(!u)return!0;var h=u.getValue(),b="";return a&&(b=_e(a)?a.checked?"true":"false":a.value),a=b,a!==h?(u.setValue(a),!0):!1}function qe(a){if(a=a||(typeof document<"u"?document:void 0),typeof a>"u")return null;try{return a.activeElement||a.body}catch{return a.body}}function kt(a,u){var h=u.checked;return O({},u,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:h??a._wrapperState.initialChecked})}function fn(a,u){var h=u.defaultValue==null?"":u.defaultValue,b=u.checked!=null?u.checked:u.defaultChecked;h=he(u.value!=null?u.value:h),a._wrapperState={initialChecked:b,initialValue:h,controlled:u.type==="checkbox"||u.type==="radio"?u.checked!=null:u.value!=null}}function nt(a,u){u=u.checked,u!=null&&k(a,"checked",u,!1)}function Yt(a,u){nt(a,u);var h=he(u.value),b=u.type;if(h!=null)b==="number"?(h===0&&a.value===""||a.value!=h)&&(a.value=""+h):a.value!==""+h&&(a.value=""+h);else if(b==="submit"||b==="reset"){a.removeAttribute("value");return}u.hasOwnProperty("value")?Pn(a,u.type,h):u.hasOwnProperty("defaultValue")&&Pn(a,u.type,he(u.defaultValue)),u.checked==null&&u.defaultChecked!=null&&(a.defaultChecked=!!u.defaultChecked)}function Ct(a,u,h){if(u.hasOwnProperty("value")||u.hasOwnProperty("defaultValue")){var b=u.type;if(!(b!=="submit"&&b!=="reset"||u.value!==void 0&&u.value!==null))return;u=""+a._wrapperState.initialValue,h||u===a.value||(a.value=u),a.defaultValue=u}h=a.name,h!==""&&(a.name=""),a.defaultChecked=!!a._wrapperState.initialChecked,h!==""&&(a.name=h)}function Pn(a,u,h){(u!=="number"||qe(a.ownerDocument)!==a)&&(h==null?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+h&&(a.defaultValue=""+h))}var Fn=Array.isArray;function on(a,u,h,b){if(a=a.options,u){u={};for(var y=0;y"+u.valueOf().toString()+"",u=Qe.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;u.firstChild;)a.appendChild(u.firstChild)}});function rt(a,u){if(u){var h=a.firstChild;if(h&&h===a.lastChild&&h.nodeType===3){h.nodeValue=u;return}}a.textContent=u}var an={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Zn=["Webkit","ms","Moz","O"];Object.keys(an).forEach(function(a){Zn.forEach(function(u){u=u+a.charAt(0).toUpperCase()+a.substring(1),an[u]=an[a]})});function Re(a,u,h){return u==null||typeof u=="boolean"||u===""?"":h||typeof u!="number"||u===0||an.hasOwnProperty(a)&&an[a]?(""+u).trim():u+"px"}function Me(a,u){a=a.style;for(var h in u)if(u.hasOwnProperty(h)){var b=h.indexOf("--")===0,y=Re(h,u[h],b);h==="float"&&(h="cssFloat"),b?a.setProperty(h,y):a[h]=y}}var Ge=O({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Ke(a,u){if(u){if(Ge[a]&&(u.children!=null||u.dangerouslySetInnerHTML!=null))throw Error(n(137,a));if(u.dangerouslySetInnerHTML!=null){if(u.children!=null)throw Error(n(60));if(typeof u.dangerouslySetInnerHTML!="object"||!("__html"in u.dangerouslySetInnerHTML))throw Error(n(61))}if(u.style!=null&&typeof u.style!="object")throw Error(n(62))}}function bt(a,u){if(a.indexOf("-")===-1)return typeof u.is=="string";switch(a){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var vt=null;function jt(a){return a=a.target||a.srcElement||window,a.correspondingUseElement&&(a=a.correspondingUseElement),a.nodeType===3?a.parentNode:a}var fr=null,Dt=null,$t=null;function qt(a){if(a=au(a)){if(typeof fr!="function")throw Error(n(280));var u=a.stateNode;u&&(u=fd(u),fr(a.stateNode,a.type,u))}}function V(a){Dt?$t?$t.push(a):$t=[a]:Dt=a}function te(){if(Dt){var a=Dt,u=$t;if($t=Dt=null,qt(a),u)for(a=0;a>>=0,a===0?32:31-(Ii(a)/pa|0)|0}var pr=64,xo=4194304;function Rs(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function vo(a,u){var h=a.pendingLanes;if(h===0)return 0;var b=0,y=a.suspendedLanes,_=a.pingedLanes,N=h&268435455;if(N!==0){var H=N&~y;H!==0?b=Rs(H):(_&=N,_!==0&&(b=Rs(_)))}else N=h&~y,N!==0?b=Rs(N):_!==0&&(b=Rs(_));if(b===0)return 0;if(u!==0&&u!==b&&(u&y)===0&&(y=b&-b,_=u&-u,y>=_||y===16&&(_&4194240)!==0))return u;if((b&4)!==0&&(b|=h&16),u=a.entangledLanes,u!==0)for(a=a.entanglements,u&=b;0h;h++)u.push(a);return u}function Os(a,u,h){a.pendingLanes|=u,u!==536870912&&(a.suspendedLanes=0,a.pingedLanes=0),a=a.eventTimes,u=31-pn(u),a[u]=h}function Yr(a,u){var h=a.pendingLanes&~u;a.pendingLanes=u,a.suspendedLanes=0,a.pingedLanes=0,a.expiredLanes&=u,a.mutableReadLanes&=u,a.entangledLanes&=u,u=a.entanglements;var b=a.eventTimes;for(a=a.expirationTimes;0=Zl),q1=" ",X1=!1;function Q1(a,u){switch(a){case"keyup":return NO.indexOf(u.keyCode)!==-1;case"keydown":return u.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Z1(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var ba=!1;function IO(a,u){switch(a){case"compositionend":return Z1(u);case"keypress":return u.which!==32?null:(X1=!0,q1);case"textInput":return a=u.data,a===q1&&X1?null:a;default:return null}}function OO(a,u){if(ba)return a==="compositionend"||!jp&&Q1(a,u)?(a=$1(),td=Pp=Ds=null,ba=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(u.ctrlKey||u.altKey||u.metaKey)||u.ctrlKey&&u.altKey){if(u.char&&1=u)return{node:h,offset:u-a};a=b}e:{for(;h;){if(h.nextSibling){h=h.nextSibling;break e}h=h.parentNode}h=void 0}h=sy(h)}}function ay(a,u){return a&&u?a===u?!0:a&&a.nodeType===3?!1:u&&u.nodeType===3?ay(a,u.parentNode):"contains"in a?a.contains(u):a.compareDocumentPosition?!!(a.compareDocumentPosition(u)&16):!1:!1}function ly(){for(var a=window,u=qe();u instanceof a.HTMLIFrameElement;){try{var h=typeof u.contentWindow.location.href=="string"}catch{h=!1}if(h)a=u.contentWindow;else break;u=qe(a.document)}return u}function Vp(a){var u=a&&a.nodeName&&a.nodeName.toLowerCase();return u&&(u==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||u==="textarea"||a.contentEditable==="true")}function zO(a){var u=ly(),h=a.focusedElem,b=a.selectionRange;if(u!==h&&h&&h.ownerDocument&&ay(h.ownerDocument.documentElement,h)){if(b!==null&&Vp(h)){if(u=b.start,a=b.end,a===void 0&&(a=u),"selectionStart"in h)h.selectionStart=u,h.selectionEnd=Math.min(a,h.value.length);else if(a=(u=h.ownerDocument||document)&&u.defaultView||window,a.getSelection){a=a.getSelection();var y=h.textContent.length,_=Math.min(b.start,y);b=b.end===void 0?_:Math.min(b.end,y),!a.extend&&_>b&&(y=b,b=_,_=y),y=oy(h,_);var N=oy(h,b);y&&N&&(a.rangeCount!==1||a.anchorNode!==y.node||a.anchorOffset!==y.offset||a.focusNode!==N.node||a.focusOffset!==N.offset)&&(u=u.createRange(),u.setStart(y.node,y.offset),a.removeAllRanges(),_>b?(a.addRange(u),a.extend(N.node,N.offset)):(u.setEnd(N.node,N.offset),a.addRange(u)))}}for(u=[],a=h;a=a.parentNode;)a.nodeType===1&&u.push({element:a,left:a.scrollLeft,top:a.scrollTop});for(typeof h.focus=="function"&&h.focus(),h=0;h=document.documentMode,Ea=null,Gp=null,nu=null,Kp=!1;function uy(a,u,h){var b=h.window===h?h.document:h.nodeType===9?h:h.ownerDocument;Kp||Ea==null||Ea!==qe(b)||(b=Ea,"selectionStart"in b&&Vp(b)?b={start:b.selectionStart,end:b.selectionEnd}:(b=(b.ownerDocument&&b.ownerDocument.defaultView||window).getSelection(),b={anchorNode:b.anchorNode,anchorOffset:b.anchorOffset,focusNode:b.focusNode,focusOffset:b.focusOffset}),nu&&tu(nu,b)||(nu=b,b=ud(Gp,"onSelect"),0Ta||(a.current=sm[Ta],sm[Ta]=null,Ta--)}function Qt(a,u){Ta++,sm[Ta]=a.current,a.current=u}var Bs={},Jn=Fs(Bs),vr=Fs(!1),_o=Bs;function Sa(a,u){var h=a.type.contextTypes;if(!h)return Bs;var b=a.stateNode;if(b&&b.__reactInternalMemoizedUnmaskedChildContext===u)return b.__reactInternalMemoizedMaskedChildContext;var y={},_;for(_ in h)y[_]=u[_];return b&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=u,a.__reactInternalMemoizedMaskedChildContext=y),y}function wr(a){return a=a.childContextTypes,a!=null}function hd(){nn(vr),nn(Jn)}function Sy(a,u,h){if(Jn.current!==Bs)throw Error(n(168));Qt(Jn,u),Qt(vr,h)}function _y(a,u,h){var b=a.stateNode;if(u=u.childContextTypes,typeof b.getChildContext!="function")return h;b=b.getChildContext();for(var y in b)if(!(y in u))throw Error(n(108,J(a)||"Unknown",y));return O({},h,b)}function pd(a){return a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||Bs,_o=Jn.current,Qt(Jn,a),Qt(vr,vr.current),!0}function Cy(a,u,h){var b=a.stateNode;if(!b)throw Error(n(169));h?(a=_y(a,u,_o),b.__reactInternalMemoizedMergedChildContext=a,nn(vr),nn(Jn),Qt(Jn,a)):nn(vr),Qt(vr,h)}var rs=null,md=!1,om=!1;function Ay(a){rs===null?rs=[a]:rs.push(a)}function JO(a){md=!0,Ay(a)}function Us(){if(!om&&rs!==null){om=!0;var a=0,u=It;try{var h=rs;for(It=1;a>=N,y-=N,is=1<<32-pn(u)+y|h<st?(zn=Je,Je=null):zn=Je.sibling;var Mt=ge(ne,Je,se[st],Ae);if(Mt===null){Je===null&&(Je=zn);break}a&&Je&&Mt.alternate===null&&u(ne,Je),q=_(Mt,q,st),Ze===null?We=Mt:Ze.sibling=Mt,Ze=Mt,Je=zn}if(st===se.length)return h(ne,Je),ln&&Ao(ne,st),We;if(Je===null){for(;stst?(zn=Je,Je=null):zn=Je.sibling;var Ys=ge(ne,Je,Mt.value,Ae);if(Ys===null){Je===null&&(Je=zn);break}a&&Je&&Ys.alternate===null&&u(ne,Je),q=_(Ys,q,st),Ze===null?We=Ys:Ze.sibling=Ys,Ze=Ys,Je=zn}if(Mt.done)return h(ne,Je),ln&&Ao(ne,st),We;if(Je===null){for(;!Mt.done;st++,Mt=se.next())Mt=xe(ne,Mt.value,Ae),Mt!==null&&(q=_(Mt,q,st),Ze===null?We=Mt:Ze.sibling=Mt,Ze=Mt);return ln&&Ao(ne,st),We}for(Je=b(ne,Je);!Mt.done;st++,Mt=se.next())Mt=Le(Je,ne,st,Mt.value,Ae),Mt!==null&&(a&&Mt.alternate!==null&&Je.delete(Mt.key===null?st:Mt.key),q=_(Mt,q,st),Ze===null?We=Mt:Ze.sibling=Mt,Ze=Mt);return a&&Je.forEach(function(MM){return u(ne,MM)}),ln&&Ao(ne,st),We}function Tn(ne,q,se,Ae){if(typeof se=="object"&&se!==null&&se.type===D&&se.key===null&&(se=se.props.children),typeof se=="object"&&se!==null){switch(se.$$typeof){case F:e:{for(var We=se.key,Ze=q;Ze!==null;){if(Ze.key===We){if(We=se.type,We===D){if(Ze.tag===7){h(ne,Ze.sibling),q=y(Ze,se.props.children),q.return=ne,ne=q;break e}}else if(Ze.elementType===We||typeof We=="object"&&We!==null&&We.$$typeof===ae&&My(We)===Ze.type){h(ne,Ze.sibling),q=y(Ze,se.props),q.ref=lu(ne,Ze,se),q.return=ne,ne=q;break e}h(ne,Ze);break}else u(ne,Ze);Ze=Ze.sibling}se.type===D?(q=Lo(se.props.children,ne.mode,Ae,se.key),q.return=ne,ne=q):(Ae=$d(se.type,se.key,se.props,null,ne.mode,Ae),Ae.ref=lu(ne,q,se),Ae.return=ne,ne=Ae)}return N(ne);case I:e:{for(Ze=se.key;q!==null;){if(q.key===Ze)if(q.tag===4&&q.stateNode.containerInfo===se.containerInfo&&q.stateNode.implementation===se.implementation){h(ne,q.sibling),q=y(q,se.children||[]),q.return=ne,ne=q;break e}else{h(ne,q);break}else u(ne,q);q=q.sibling}q=rg(se,ne.mode,Ae),q.return=ne,ne=q}return N(ne);case ae:return Ze=se._init,Tn(ne,q,Ze(se._payload),Ae)}if(Fn(se))return ze(ne,q,se,Ae);if(W(se))return $e(ne,q,se,Ae);yd(ne,se)}return typeof se=="string"&&se!==""||typeof se=="number"?(se=""+se,q!==null&&q.tag===6?(h(ne,q.sibling),q=y(q,se),q.return=ne,ne=q):(h(ne,q),q=ng(se,ne.mode,Ae),q.return=ne,ne=q),N(ne)):h(ne,q)}return Tn}var ka=Dy(!0),Ly=Dy(!1),xd=Fs(null),vd=null,Na=null,fm=null;function hm(){fm=Na=vd=null}function pm(a){var u=xd.current;nn(xd),a._currentValue=u}function mm(a,u,h){for(;a!==null;){var b=a.alternate;if((a.childLanes&u)!==u?(a.childLanes|=u,b!==null&&(b.childLanes|=u)):b!==null&&(b.childLanes&u)!==u&&(b.childLanes|=u),a===h)break;a=a.return}}function Ra(a,u){vd=a,fm=Na=null,a=a.dependencies,a!==null&&a.firstContext!==null&&((a.lanes&u)!==0&&(Tr=!0),a.firstContext=null)}function Qr(a){var u=a._currentValue;if(fm!==a)if(a={context:a,memoizedValue:u,next:null},Na===null){if(vd===null)throw Error(n(308));Na=a,vd.dependencies={lanes:0,firstContext:a}}else Na=Na.next=a;return u}var ko=null;function gm(a){ko===null?ko=[a]:ko.push(a)}function Py(a,u,h,b){var y=u.interleaved;return y===null?(h.next=h,gm(u)):(h.next=y.next,y.next=h),u.interleaved=h,os(a,b)}function os(a,u){a.lanes|=u;var h=a.alternate;for(h!==null&&(h.lanes|=u),h=a,a=a.return;a!==null;)a.childLanes|=u,h=a.alternate,h!==null&&(h.childLanes|=u),h=a,a=a.return;return h.tag===3?h.stateNode:null}var Hs=!1;function bm(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Fy(a,u){a=a.updateQueue,u.updateQueue===a&&(u.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function as(a,u){return{eventTime:a,lane:u,tag:0,payload:null,callback:null,next:null}}function zs(a,u,h){var b=a.updateQueue;if(b===null)return null;if(b=b.shared,(Ot&2)!==0){var y=b.pending;return y===null?u.next=u:(u.next=y.next,y.next=u),b.pending=u,os(a,h)}return y=b.interleaved,y===null?(u.next=u,gm(b)):(u.next=y.next,y.next=u),b.interleaved=u,os(a,h)}function wd(a,u,h){if(u=u.updateQueue,u!==null&&(u=u.shared,(h&4194240)!==0)){var b=u.lanes;b&=a.pendingLanes,h|=b,u.lanes=h,Vl(a,h)}}function By(a,u){var h=a.updateQueue,b=a.alternate;if(b!==null&&(b=b.updateQueue,h===b)){var y=null,_=null;if(h=h.firstBaseUpdate,h!==null){do{var N={eventTime:h.eventTime,lane:h.lane,tag:h.tag,payload:h.payload,callback:h.callback,next:null};_===null?y=_=N:_=_.next=N,h=h.next}while(h!==null);_===null?y=_=u:_=_.next=u}else y=_=u;h={baseState:b.baseState,firstBaseUpdate:y,lastBaseUpdate:_,shared:b.shared,effects:b.effects},a.updateQueue=h;return}a=h.lastBaseUpdate,a===null?h.firstBaseUpdate=u:a.next=u,h.lastBaseUpdate=u}function Td(a,u,h,b){var y=a.updateQueue;Hs=!1;var _=y.firstBaseUpdate,N=y.lastBaseUpdate,H=y.shared.pending;if(H!==null){y.shared.pending=null;var K=H,le=K.next;K.next=null,N===null?_=le:N.next=le,N=K;var Ee=a.alternate;Ee!==null&&(Ee=Ee.updateQueue,H=Ee.lastBaseUpdate,H!==N&&(H===null?Ee.firstBaseUpdate=le:H.next=le,Ee.lastBaseUpdate=K))}if(_!==null){var xe=y.baseState;N=0,Ee=le=K=null,H=_;do{var ge=H.lane,Le=H.eventTime;if((b&ge)===ge){Ee!==null&&(Ee=Ee.next={eventTime:Le,lane:0,tag:H.tag,payload:H.payload,callback:H.callback,next:null});e:{var ze=a,$e=H;switch(ge=u,Le=h,$e.tag){case 1:if(ze=$e.payload,typeof ze=="function"){xe=ze.call(Le,xe,ge);break e}xe=ze;break e;case 3:ze.flags=ze.flags&-65537|128;case 0:if(ze=$e.payload,ge=typeof ze=="function"?ze.call(Le,xe,ge):ze,ge==null)break e;xe=O({},xe,ge);break e;case 2:Hs=!0}}H.callback!==null&&H.lane!==0&&(a.flags|=64,ge=y.effects,ge===null?y.effects=[H]:ge.push(H))}else Le={eventTime:Le,lane:ge,tag:H.tag,payload:H.payload,callback:H.callback,next:null},Ee===null?(le=Ee=Le,K=xe):Ee=Ee.next=Le,N|=ge;if(H=H.next,H===null){if(H=y.shared.pending,H===null)break;ge=H,H=ge.next,ge.next=null,y.lastBaseUpdate=ge,y.shared.pending=null}}while(!0);if(Ee===null&&(K=xe),y.baseState=K,y.firstBaseUpdate=le,y.lastBaseUpdate=Ee,u=y.shared.interleaved,u!==null){y=u;do N|=y.lane,y=y.next;while(y!==u)}else _===null&&(y.shared.lanes=0);Io|=N,a.lanes=N,a.memoizedState=xe}}function Uy(a,u,h){if(a=u.effects,u.effects=null,a!==null)for(u=0;uh?h:4,a(!0);var b=wm.transition;wm.transition={};try{a(!1),u()}finally{It=h,wm.transition=b}}function ix(){return Zr().memoizedState}function rM(a,u,h){var b=Vs(a);if(h={lane:b,action:h,hasEagerState:!1,eagerState:null,next:null},sx(a))ox(u,h);else if(h=Py(a,u,h,b),h!==null){var y=gr();mi(h,a,b,y),ax(h,u,b)}}function iM(a,u,h){var b=Vs(a),y={lane:b,action:h,hasEagerState:!1,eagerState:null,next:null};if(sx(a))ox(u,y);else{var _=a.alternate;if(a.lanes===0&&(_===null||_.lanes===0)&&(_=u.lastRenderedReducer,_!==null))try{var N=u.lastRenderedState,H=_(N,h);if(y.hasEagerState=!0,y.eagerState=H,ci(H,N)){var K=u.interleaved;K===null?(y.next=y,gm(u)):(y.next=K.next,K.next=y),u.interleaved=y;return}}catch{}finally{}h=Py(a,u,y,b),h!==null&&(y=gr(),mi(h,a,b,y),ax(h,u,b))}}function sx(a){var u=a.alternate;return a===gn||u!==null&&u===gn}function ox(a,u){fu=Cd=!0;var h=a.pending;h===null?u.next=u:(u.next=h.next,h.next=u),a.pending=u}function ax(a,u,h){if((h&4194240)!==0){var b=u.lanes;b&=a.pendingLanes,h|=b,u.lanes=h,Vl(a,h)}}var Nd={readContext:Qr,useCallback:er,useContext:er,useEffect:er,useImperativeHandle:er,useInsertionEffect:er,useLayoutEffect:er,useMemo:er,useReducer:er,useRef:er,useState:er,useDebugValue:er,useDeferredValue:er,useTransition:er,useMutableSource:er,useSyncExternalStore:er,useId:er,unstable_isNewReconciler:!1},sM={readContext:Qr,useCallback:function(a,u){return Li().memoizedState=[a,u===void 0?null:u],a},useContext:Qr,useEffect:Xy,useImperativeHandle:function(a,u,h){return h=h!=null?h.concat([a]):null,Ad(4194308,4,Jy.bind(null,u,a),h)},useLayoutEffect:function(a,u){return Ad(4194308,4,a,u)},useInsertionEffect:function(a,u){return Ad(4,2,a,u)},useMemo:function(a,u){var h=Li();return u=u===void 0?null:u,a=a(),h.memoizedState=[a,u],a},useReducer:function(a,u,h){var b=Li();return u=h!==void 0?h(u):u,b.memoizedState=b.baseState=u,a={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:u},b.queue=a,a=a.dispatch=rM.bind(null,gn,a),[b.memoizedState,a]},useRef:function(a){var u=Li();return a={current:a},u.memoizedState=a},useState:Yy,useDebugValue:Nm,useDeferredValue:function(a){return Li().memoizedState=a},useTransition:function(){var a=Yy(!1),u=a[0];return a=nM.bind(null,a[1]),Li().memoizedState=a,[u,a]},useMutableSource:function(){},useSyncExternalStore:function(a,u,h){var b=gn,y=Li();if(ln){if(h===void 0)throw Error(n(407));h=h()}else{if(h=u(),Hn===null)throw Error(n(349));(Ro&30)!==0||$y(b,u,h)}y.memoizedState=h;var _={value:h,getSnapshot:u};return y.queue=_,Xy(Vy.bind(null,b,_,a),[a]),b.flags|=2048,mu(9,Wy.bind(null,b,_,h,u),void 0,null),h},useId:function(){var a=Li(),u=Hn.identifierPrefix;if(ln){var h=ss,b=is;h=(b&~(1<<32-pn(b)-1)).toString(32)+h,u=":"+u+"R"+h,h=hu++,0<\/script>",a=a.removeChild(a.firstChild)):typeof b.is=="string"?a=N.createElement(h,{is:b.is}):(a=N.createElement(h),h==="select"&&(N=a,b.multiple?N.multiple=!0:b.size&&(N.size=b.size))):a=N.createElementNS(a,h),a[Mi]=u,a[ou]=b,Ax(a,u,!1,!1),u.stateNode=a;e:{switch(N=bt(h,b),h){case"dialog":tn("cancel",a),tn("close",a),y=b;break;case"iframe":case"object":case"embed":tn("load",a),y=b;break;case"video":case"audio":for(y=0;yLa&&(u.flags|=128,b=!0,gu(_,!1),u.lanes=4194304)}else{if(!b)if(a=Sd(N),a!==null){if(u.flags|=128,b=!0,h=a.updateQueue,h!==null&&(u.updateQueue=h,u.flags|=4),gu(_,!0),_.tail===null&&_.tailMode==="hidden"&&!N.alternate&&!ln)return tr(u),null}else 2*hn()-_.renderingStartTime>La&&h!==1073741824&&(u.flags|=128,b=!0,gu(_,!1),u.lanes=4194304);_.isBackwards?(N.sibling=u.child,u.child=N):(h=_.last,h!==null?h.sibling=N:u.child=N,_.last=N)}return _.tail!==null?(u=_.tail,_.rendering=u,_.tail=u.sibling,_.renderingStartTime=hn(),u.sibling=null,h=mn.current,Qt(mn,b?h&1|2:h&1),u):(tr(u),null);case 22:case 23:return Jm(),b=u.memoizedState!==null,a!==null&&a.memoizedState!==null!==b&&(u.flags|=8192),b&&(u.mode&1)!==0?(Fr&1073741824)!==0&&(tr(u),u.subtreeFlags&6&&(u.flags|=8192)):tr(u),null;case 24:return null;case 25:return null}throw Error(n(156,u.tag))}function hM(a,u){switch(lm(u),u.tag){case 1:return wr(u.type)&&hd(),a=u.flags,a&65536?(u.flags=a&-65537|128,u):null;case 3:return Ia(),nn(vr),nn(Jn),vm(),a=u.flags,(a&65536)!==0&&(a&128)===0?(u.flags=a&-65537|128,u):null;case 5:return ym(u),null;case 13:if(nn(mn),a=u.memoizedState,a!==null&&a.dehydrated!==null){if(u.alternate===null)throw Error(n(340));Aa()}return a=u.flags,a&65536?(u.flags=a&-65537|128,u):null;case 19:return nn(mn),null;case 4:return Ia(),null;case 10:return pm(u.type._context),null;case 22:case 23:return Jm(),null;case 24:return null;default:return null}}var Md=!1,nr=!1,pM=typeof WeakSet=="function"?WeakSet:Set,Ue=null;function Ma(a,u){var h=a.ref;if(h!==null)if(typeof h=="function")try{h(null)}catch(b){yn(a,u,b)}else h.current=null}function zm(a,u,h){try{h()}catch(b){yn(a,u,b)}}var Rx=!1;function mM(a,u){if(Jp=Jc,a=ly(),Vp(a)){if("selectionStart"in a)var h={start:a.selectionStart,end:a.selectionEnd};else e:{h=(h=a.ownerDocument)&&h.defaultView||window;var b=h.getSelection&&h.getSelection();if(b&&b.rangeCount!==0){h=b.anchorNode;var y=b.anchorOffset,_=b.focusNode;b=b.focusOffset;try{h.nodeType,_.nodeType}catch{h=null;break e}var N=0,H=-1,K=-1,le=0,Ee=0,xe=a,ge=null;t:for(;;){for(var Le;xe!==h||y!==0&&xe.nodeType!==3||(H=N+y),xe!==_||b!==0&&xe.nodeType!==3||(K=N+b),xe.nodeType===3&&(N+=xe.nodeValue.length),(Le=xe.firstChild)!==null;)ge=xe,xe=Le;for(;;){if(xe===a)break t;if(ge===h&&++le===y&&(H=N),ge===_&&++Ee===b&&(K=N),(Le=xe.nextSibling)!==null)break;xe=ge,ge=xe.parentNode}xe=Le}h=H===-1||K===-1?null:{start:H,end:K}}else h=null}h=h||{start:0,end:0}}else h=null;for(em={focusedElem:a,selectionRange:h},Jc=!1,Ue=u;Ue!==null;)if(u=Ue,a=u.child,(u.subtreeFlags&1028)!==0&&a!==null)a.return=u,Ue=a;else for(;Ue!==null;){u=Ue;try{var ze=u.alternate;if((u.flags&1024)!==0)switch(u.tag){case 0:case 11:case 15:break;case 1:if(ze!==null){var $e=ze.memoizedProps,Tn=ze.memoizedState,ne=u.stateNode,q=ne.getSnapshotBeforeUpdate(u.elementType===u.type?$e:fi(u.type,$e),Tn);ne.__reactInternalSnapshotBeforeUpdate=q}break;case 3:var se=u.stateNode.containerInfo;se.nodeType===1?se.textContent="":se.nodeType===9&&se.documentElement&&se.removeChild(se.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(n(163))}}catch(Ae){yn(u,u.return,Ae)}if(a=u.sibling,a!==null){a.return=u.return,Ue=a;break}Ue=u.return}return ze=Rx,Rx=!1,ze}function bu(a,u,h){var b=u.updateQueue;if(b=b!==null?b.lastEffect:null,b!==null){var y=b=b.next;do{if((y.tag&a)===a){var _=y.destroy;y.destroy=void 0,_!==void 0&&zm(u,h,_)}y=y.next}while(y!==b)}}function Dd(a,u){if(u=u.updateQueue,u=u!==null?u.lastEffect:null,u!==null){var h=u=u.next;do{if((h.tag&a)===a){var b=h.create;h.destroy=b()}h=h.next}while(h!==u)}}function jm(a){var u=a.ref;if(u!==null){var h=a.stateNode;switch(a.tag){case 5:a=h;break;default:a=h}typeof u=="function"?u(a):u.current=a}}function Ix(a){var u=a.alternate;u!==null&&(a.alternate=null,Ix(u)),a.child=null,a.deletions=null,a.sibling=null,a.tag===5&&(u=a.stateNode,u!==null&&(delete u[Mi],delete u[ou],delete u[im],delete u[QO],delete u[ZO])),a.stateNode=null,a.return=null,a.dependencies=null,a.memoizedProps=null,a.memoizedState=null,a.pendingProps=null,a.stateNode=null,a.updateQueue=null}function Ox(a){return a.tag===5||a.tag===3||a.tag===4}function Mx(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||Ox(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function $m(a,u,h){var b=a.tag;if(b===5||b===6)a=a.stateNode,u?h.nodeType===8?h.parentNode.insertBefore(a,u):h.insertBefore(a,u):(h.nodeType===8?(u=h.parentNode,u.insertBefore(a,h)):(u=h,u.appendChild(a)),h=h._reactRootContainer,h!=null||u.onclick!==null||(u.onclick=dd));else if(b!==4&&(a=a.child,a!==null))for($m(a,u,h),a=a.sibling;a!==null;)$m(a,u,h),a=a.sibling}function Wm(a,u,h){var b=a.tag;if(b===5||b===6)a=a.stateNode,u?h.insertBefore(a,u):h.appendChild(a);else if(b!==4&&(a=a.child,a!==null))for(Wm(a,u,h),a=a.sibling;a!==null;)Wm(a,u,h),a=a.sibling}var Vn=null,hi=!1;function js(a,u,h){for(h=h.child;h!==null;)Dx(a,u,h),h=h.sibling}function Dx(a,u,h){if(Tt&&typeof Tt.onCommitFiberUnmount=="function")try{Tt.onCommitFiberUnmount(mt,h)}catch{}switch(h.tag){case 5:nr||Ma(h,u);case 6:var b=Vn,y=hi;Vn=null,js(a,u,h),Vn=b,hi=y,Vn!==null&&(hi?(a=Vn,h=h.stateNode,a.nodeType===8?a.parentNode.removeChild(h):a.removeChild(h)):Vn.removeChild(h.stateNode));break;case 18:Vn!==null&&(hi?(a=Vn,h=h.stateNode,a.nodeType===8?rm(a.parentNode,h):a.nodeType===1&&rm(a,h),ql(a)):rm(Vn,h.stateNode));break;case 4:b=Vn,y=hi,Vn=h.stateNode.containerInfo,hi=!0,js(a,u,h),Vn=b,hi=y;break;case 0:case 11:case 14:case 15:if(!nr&&(b=h.updateQueue,b!==null&&(b=b.lastEffect,b!==null))){y=b=b.next;do{var _=y,N=_.destroy;_=_.tag,N!==void 0&&((_&2)!==0||(_&4)!==0)&&zm(h,u,N),y=y.next}while(y!==b)}js(a,u,h);break;case 1:if(!nr&&(Ma(h,u),b=h.stateNode,typeof b.componentWillUnmount=="function"))try{b.props=h.memoizedProps,b.state=h.memoizedState,b.componentWillUnmount()}catch(H){yn(h,u,H)}js(a,u,h);break;case 21:js(a,u,h);break;case 22:h.mode&1?(nr=(b=nr)||h.memoizedState!==null,js(a,u,h),nr=b):js(a,u,h);break;default:js(a,u,h)}}function Lx(a){var u=a.updateQueue;if(u!==null){a.updateQueue=null;var h=a.stateNode;h===null&&(h=a.stateNode=new pM),u.forEach(function(b){var y=SM.bind(null,a,b);h.has(b)||(h.add(b),b.then(y,y))})}}function pi(a,u){var h=u.deletions;if(h!==null)for(var b=0;by&&(y=N),b&=~_}if(b=y,b=hn()-b,b=(120>b?120:480>b?480:1080>b?1080:1920>b?1920:3e3>b?3e3:4320>b?4320:1960*bM(b/1960))-b,10a?16:a,Ws===null)var b=!1;else{if(a=Ws,Ws=null,Ud=0,(Ot&6)!==0)throw Error(n(331));var y=Ot;for(Ot|=4,Ue=a.current;Ue!==null;){var _=Ue,N=_.child;if((Ue.flags&16)!==0){var H=_.deletions;if(H!==null){for(var K=0;Khn()-Km?Mo(a,0):Gm|=h),_r(a,u)}function Yx(a,u){u===0&&((a.mode&1)===0?u=1:(u=xo,xo<<=1,(xo&130023424)===0&&(xo=4194304)));var h=gr();a=os(a,u),a!==null&&(Os(a,u,h),_r(a,h))}function TM(a){var u=a.memoizedState,h=0;u!==null&&(h=u.retryLane),Yx(a,h)}function SM(a,u){var h=0;switch(a.tag){case 13:var b=a.stateNode,y=a.memoizedState;y!==null&&(h=y.retryLane);break;case 19:b=a.stateNode;break;default:throw Error(n(314))}b!==null&&b.delete(u),Yx(a,h)}var qx;qx=function(a,u,h){if(a!==null)if(a.memoizedProps!==u.pendingProps||vr.current)Tr=!0;else{if((a.lanes&h)===0&&(u.flags&128)===0)return Tr=!1,dM(a,u,h);Tr=(a.flags&131072)!==0}else Tr=!1,ln&&(u.flags&1048576)!==0&&ky(u,bd,u.index);switch(u.lanes=0,u.tag){case 2:var b=u.type;Od(a,u),a=u.pendingProps;var y=Sa(u,Jn.current);Ra(u,h),y=Sm(null,u,b,a,y,h);var _=_m();return u.flags|=1,typeof y=="object"&&y!==null&&typeof y.render=="function"&&y.$$typeof===void 0?(u.tag=1,u.memoizedState=null,u.updateQueue=null,wr(b)?(_=!0,pd(u)):_=!1,u.memoizedState=y.state!==null&&y.state!==void 0?y.state:null,bm(u),y.updater=Rd,u.stateNode=y,y._reactInternals=u,Im(u,b,a,h),u=Lm(null,u,b,!0,_,h)):(u.tag=0,ln&&_&&am(u),mr(null,u,y,h),u=u.child),u;case 16:b=u.elementType;e:{switch(Od(a,u),a=u.pendingProps,y=b._init,b=y(b._payload),u.type=b,y=u.tag=CM(b),a=fi(b,a),y){case 0:u=Dm(null,u,b,a,h);break e;case 1:u=vx(null,u,b,a,h);break e;case 11:u=gx(null,u,b,a,h);break e;case 14:u=bx(null,u,b,fi(b.type,a),h);break e}throw Error(n(306,b,""))}return u;case 0:return b=u.type,y=u.pendingProps,y=u.elementType===b?y:fi(b,y),Dm(a,u,b,y,h);case 1:return b=u.type,y=u.pendingProps,y=u.elementType===b?y:fi(b,y),vx(a,u,b,y,h);case 3:e:{if(wx(u),a===null)throw Error(n(387));b=u.pendingProps,_=u.memoizedState,y=_.element,Fy(a,u),Td(u,b,null,h);var N=u.memoizedState;if(b=N.element,_.isDehydrated)if(_={element:b,isDehydrated:!1,cache:N.cache,pendingSuspenseBoundaries:N.pendingSuspenseBoundaries,transitions:N.transitions},u.updateQueue.baseState=_,u.memoizedState=_,u.flags&256){y=Oa(Error(n(423)),u),u=Tx(a,u,b,h,y);break e}else if(b!==y){y=Oa(Error(n(424)),u),u=Tx(a,u,b,h,y);break e}else for(Pr=Ps(u.stateNode.containerInfo.firstChild),Lr=u,ln=!0,di=null,h=Ly(u,null,b,h),u.child=h;h;)h.flags=h.flags&-3|4096,h=h.sibling;else{if(Aa(),b===y){u=ls(a,u,h);break e}mr(a,u,b,h)}u=u.child}return u;case 5:return Hy(u),a===null&&cm(u),b=u.type,y=u.pendingProps,_=a!==null?a.memoizedProps:null,N=y.children,tm(b,y)?N=null:_!==null&&tm(b,_)&&(u.flags|=32),xx(a,u),mr(a,u,N,h),u.child;case 6:return a===null&&cm(u),null;case 13:return Sx(a,u,h);case 4:return Em(u,u.stateNode.containerInfo),b=u.pendingProps,a===null?u.child=ka(u,null,b,h):mr(a,u,b,h),u.child;case 11:return b=u.type,y=u.pendingProps,y=u.elementType===b?y:fi(b,y),gx(a,u,b,y,h);case 7:return mr(a,u,u.pendingProps,h),u.child;case 8:return mr(a,u,u.pendingProps.children,h),u.child;case 12:return mr(a,u,u.pendingProps.children,h),u.child;case 10:e:{if(b=u.type._context,y=u.pendingProps,_=u.memoizedProps,N=y.value,Qt(xd,b._currentValue),b._currentValue=N,_!==null)if(ci(_.value,N)){if(_.children===y.children&&!vr.current){u=ls(a,u,h);break e}}else for(_=u.child,_!==null&&(_.return=u);_!==null;){var H=_.dependencies;if(H!==null){N=_.child;for(var K=H.firstContext;K!==null;){if(K.context===b){if(_.tag===1){K=as(-1,h&-h),K.tag=2;var le=_.updateQueue;if(le!==null){le=le.shared;var Ee=le.pending;Ee===null?K.next=K:(K.next=Ee.next,Ee.next=K),le.pending=K}}_.lanes|=h,K=_.alternate,K!==null&&(K.lanes|=h),mm(_.return,h,u),H.lanes|=h;break}K=K.next}}else if(_.tag===10)N=_.type===u.type?null:_.child;else if(_.tag===18){if(N=_.return,N===null)throw Error(n(341));N.lanes|=h,H=N.alternate,H!==null&&(H.lanes|=h),mm(N,h,u),N=_.sibling}else N=_.child;if(N!==null)N.return=_;else for(N=_;N!==null;){if(N===u){N=null;break}if(_=N.sibling,_!==null){_.return=N.return,N=_;break}N=N.return}_=N}mr(a,u,y.children,h),u=u.child}return u;case 9:return y=u.type,b=u.pendingProps.children,Ra(u,h),y=Qr(y),b=b(y),u.flags|=1,mr(a,u,b,h),u.child;case 14:return b=u.type,y=fi(b,u.pendingProps),y=fi(b.type,y),bx(a,u,b,y,h);case 15:return Ex(a,u,u.type,u.pendingProps,h);case 17:return b=u.type,y=u.pendingProps,y=u.elementType===b?y:fi(b,y),Od(a,u),u.tag=1,wr(b)?(a=!0,pd(u)):a=!1,Ra(u,h),ux(u,b,y),Im(u,b,y,h),Lm(null,u,b,!0,a,h);case 19:return Cx(a,u,h);case 22:return yx(a,u,h)}throw Error(n(156,u.tag))};function Xx(a,u){return Kc(a,u)}function _M(a,u,h,b){this.tag=a,this.key=h,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=u,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=b,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ei(a,u,h,b){return new _M(a,u,h,b)}function tg(a){return a=a.prototype,!(!a||!a.isReactComponent)}function CM(a){if(typeof a=="function")return tg(a)?1:0;if(a!=null){if(a=a.$$typeof,a===z)return 11;if(a===ee)return 14}return 2}function Ks(a,u){var h=a.alternate;return h===null?(h=ei(a.tag,u,a.key,a.mode),h.elementType=a.elementType,h.type=a.type,h.stateNode=a.stateNode,h.alternate=a,a.alternate=h):(h.pendingProps=u,h.type=a.type,h.flags=0,h.subtreeFlags=0,h.deletions=null),h.flags=a.flags&14680064,h.childLanes=a.childLanes,h.lanes=a.lanes,h.child=a.child,h.memoizedProps=a.memoizedProps,h.memoizedState=a.memoizedState,h.updateQueue=a.updateQueue,u=a.dependencies,h.dependencies=u===null?null:{lanes:u.lanes,firstContext:u.firstContext},h.sibling=a.sibling,h.index=a.index,h.ref=a.ref,h}function $d(a,u,h,b,y,_){var N=2;if(b=a,typeof a=="function")tg(a)&&(N=1);else if(typeof a=="string")N=5;else e:switch(a){case D:return Lo(h.children,y,_,u);case G:N=8,y|=8;break;case X:return a=ei(12,h,u,y|2),a.elementType=X,a.lanes=_,a;case ie:return a=ei(13,h,u,y),a.elementType=ie,a.lanes=_,a;case Z:return a=ei(19,h,u,y),a.elementType=Z,a.lanes=_,a;case de:return Wd(h,y,_,u);default:if(typeof a=="object"&&a!==null)switch(a.$$typeof){case P:N=10;break e;case Y:N=9;break e;case z:N=11;break e;case ee:N=14;break e;case ae:N=16,b=null;break e}throw Error(n(130,a==null?a:typeof a,""))}return u=ei(N,h,u,y),u.elementType=a,u.type=b,u.lanes=_,u}function Lo(a,u,h,b){return a=ei(7,a,b,u),a.lanes=h,a}function Wd(a,u,h,b){return a=ei(22,a,b,u),a.elementType=de,a.lanes=h,a.stateNode={isHidden:!1},a}function ng(a,u,h){return a=ei(6,a,null,u),a.lanes=h,a}function rg(a,u,h){return u=ei(4,a.children!==null?a.children:[],a.key,u),u.lanes=h,u.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation},u}function AM(a,u,h,b,y){this.tag=u,this.containerInfo=a,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Is(0),this.expirationTimes=Is(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Is(0),this.identifierPrefix=b,this.onRecoverableError=y,this.mutableSourceEagerHydrationData=null}function ig(a,u,h,b,y,_,N,H,K){return a=new AM(a,u,h,H,K),u===1?(u=1,_===!0&&(u|=8)):u=0,_=ei(3,null,null,u),a.current=_,_.stateNode=a,_.memoizedState={element:b,isDehydrated:h,cache:null,transitions:null,pendingSuspenseBoundaries:null},bm(_),a}function kM(a,u,h){var b=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),cg.exports=HM(),cg.exports}var cv;function zM(){if(cv)return Qd;cv=1;var t=ZS();return Qd.createRoot=t.createRoot,Qd.hydrateRoot=t.hydrateRoot,Qd}var jM=zM();const $M=()=>{const e=window.location.pathname.split("/").filter(Boolean);return e.length>0&&!["chat","docs","api","healthz"].includes(e[0])?"/"+e[0]:""},lo=$M(),Zb=async(t,e={})=>{try{const n=await fetch(t,e);if(!n.ok)throw new Error(`HTTP error! Status: ${n.status}`);return e.method==="PATCH"||e.method==="DELETE"?void 0:await n.json()}catch(n){throw console.error("Fetch error:",n),n}},dv=async(t,e)=>Zb(`${lo}/${t}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),WM=async(t,e)=>Zb(`${lo}/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),JS=async t=>Zb(`${lo}/${t}`,{method:"DELETE"});var Ac=ZS();const e_=_s(Ac);var VM=t=>{switch(t){case"success":return YM;case"info":return XM;case"warning":return qM;case"error":return QM;default:return null}},GM=Array(12).fill(0),KM=({visible:t,className:e})=>we.createElement("div",{className:["sonner-loading-wrapper",e].filter(Boolean).join(" "),"data-visible":t},we.createElement("div",{className:"sonner-spinner"},GM.map((n,r)=>we.createElement("div",{className:"sonner-loading-bar",key:`spinner-bar-${r}`})))),YM=we.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor",height:"20",width:"20"},we.createElement("path",{fillRule:"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z",clipRule:"evenodd"})),qM=we.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor",height:"20",width:"20"},we.createElement("path",{fillRule:"evenodd",d:"M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z",clipRule:"evenodd"})),XM=we.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor",height:"20",width:"20"},we.createElement("path",{fillRule:"evenodd",d:"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z",clipRule:"evenodd"})),QM=we.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor",height:"20",width:"20"},we.createElement("path",{fillRule:"evenodd",d:"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",clipRule:"evenodd"})),ZM=we.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"},we.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),we.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"})),JM=()=>{let[t,e]=we.useState(document.hidden);return we.useEffect(()=>{let n=()=>{e(document.hidden)};return document.addEventListener("visibilitychange",n),()=>window.removeEventListener("visibilitychange",n)},[]),t},C0=1,eD=class{constructor(){this.subscribe=t=>(this.subscribers.push(t),()=>{let e=this.subscribers.indexOf(t);this.subscribers.splice(e,1)}),this.publish=t=>{this.subscribers.forEach(e=>e(t))},this.addToast=t=>{this.publish(t),this.toasts=[...this.toasts,t]},this.create=t=>{var e;let{message:n,...r}=t,i=typeof t?.id=="number"||((e=t.id)==null?void 0:e.length)>0?t.id:C0++,s=this.toasts.find(l=>l.id===i),o=t.dismissible===void 0?!0:t.dismissible;return this.dismissedToasts.has(i)&&this.dismissedToasts.delete(i),s?this.toasts=this.toasts.map(l=>l.id===i?(this.publish({...l,...t,id:i,title:n}),{...l,...t,id:i,dismissible:o,title:n}):l):this.addToast({title:n,...r,dismissible:o,id:i}),i},this.dismiss=t=>(this.dismissedToasts.add(t),t||this.toasts.forEach(e=>{this.subscribers.forEach(n=>n({id:e.id,dismiss:!0}))}),this.subscribers.forEach(e=>e({id:t,dismiss:!0})),t),this.message=(t,e)=>this.create({...e,message:t}),this.error=(t,e)=>this.create({...e,message:t,type:"error"}),this.success=(t,e)=>this.create({...e,type:"success",message:t}),this.info=(t,e)=>this.create({...e,type:"info",message:t}),this.warning=(t,e)=>this.create({...e,type:"warning",message:t}),this.loading=(t,e)=>this.create({...e,type:"loading",message:t}),this.promise=(t,e)=>{if(!e)return;let n;e.loading!==void 0&&(n=this.create({...e,promise:t,type:"loading",message:e.loading,description:typeof e.description!="function"?e.description:void 0}));let r=t instanceof Promise?t:t(),i=n!==void 0,s,o=r.then(async c=>{if(s=["resolve",c],we.isValidElement(c))i=!1,this.create({id:n,type:"default",message:c});else if(nD(c)&&!c.ok){i=!1;let d=typeof e.error=="function"?await e.error(`HTTP error! status: ${c.status}`):e.error,f=typeof e.description=="function"?await e.description(`HTTP error! status: ${c.status}`):e.description;this.create({id:n,type:"error",message:d,description:f})}else if(e.success!==void 0){i=!1;let d=typeof e.success=="function"?await e.success(c):e.success,f=typeof e.description=="function"?await e.description(c):e.description;this.create({id:n,type:"success",message:d,description:f})}}).catch(async c=>{if(s=["reject",c],e.error!==void 0){i=!1;let d=typeof e.error=="function"?await e.error(c):e.error,f=typeof e.description=="function"?await e.description(c):e.description;this.create({id:n,type:"error",message:d,description:f})}}).finally(()=>{var c;i&&(this.dismiss(n),n=void 0),(c=e.finally)==null||c.call(e)}),l=()=>new Promise((c,d)=>o.then(()=>s[0]==="reject"?d(s[1]):c(s[1])).catch(d));return typeof n!="string"&&typeof n!="number"?{unwrap:l}:Object.assign(n,{unwrap:l})},this.custom=(t,e)=>{let n=e?.id||C0++;return this.create({jsx:t(n),id:n,...e}),n},this.getActiveToasts=()=>this.toasts.filter(t=>!this.dismissedToasts.has(t.id)),this.subscribers=[],this.toasts=[],this.dismissedToasts=new Set}},Rr=new eD,tD=(t,e)=>{let n=e?.id||C0++;return Rr.addToast({title:t,...e,id:n}),n},nD=t=>t&&typeof t=="object"&&"ok"in t&&typeof t.ok=="boolean"&&"status"in t&&typeof t.status=="number",rD=tD,iD=()=>Rr.toasts,sD=()=>Rr.getActiveToasts(),Ir=Object.assign(rD,{success:Rr.success,info:Rr.info,warning:Rr.warning,error:Rr.error,custom:Rr.custom,message:Rr.message,promise:Rr.promise,dismiss:Rr.dismiss,loading:Rr.loading},{getHistory:iD,getToasts:sD});function oD(t,{insertAt:e}={}){if(typeof document>"u")return;let n=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css",e==="top"&&n.firstChild?n.insertBefore(r,n.firstChild):n.appendChild(r),r.styleSheet?r.styleSheet.cssText=t:r.appendChild(document.createTextNode(t))}oD(`:where(html[dir="ltr"]),:where([data-sonner-toaster][dir="ltr"]){--toast-icon-margin-start: -3px;--toast-icon-margin-end: 4px;--toast-svg-margin-start: -1px;--toast-svg-margin-end: 0px;--toast-button-margin-start: auto;--toast-button-margin-end: 0;--toast-close-button-start: 0;--toast-close-button-end: unset;--toast-close-button-transform: translate(-35%, -35%)}:where(html[dir="rtl"]),:where([data-sonner-toaster][dir="rtl"]){--toast-icon-margin-start: 4px;--toast-icon-margin-end: -3px;--toast-svg-margin-start: 0px;--toast-svg-margin-end: -1px;--toast-button-margin-start: 0;--toast-button-margin-end: auto;--toast-close-button-start: unset;--toast-close-button-end: 0;--toast-close-button-transform: translate(35%, -35%)}:where([data-sonner-toaster]){position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1: hsl(0, 0%, 99%);--gray2: hsl(0, 0%, 97.3%);--gray3: hsl(0, 0%, 95.1%);--gray4: hsl(0, 0%, 93%);--gray5: hsl(0, 0%, 90.9%);--gray6: hsl(0, 0%, 88.7%);--gray7: hsl(0, 0%, 85.8%);--gray8: hsl(0, 0%, 78%);--gray9: hsl(0, 0%, 56.1%);--gray10: hsl(0, 0%, 52.3%);--gray11: hsl(0, 0%, 43.5%);--gray12: hsl(0, 0%, 9%);--border-radius: 8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:none;z-index:999999999;transition:transform .4s ease}:where([data-sonner-toaster][data-lifted="true"]){transform:translateY(-10px)}@media (hover: none) and (pointer: coarse){:where([data-sonner-toaster][data-lifted="true"]){transform:none}}:where([data-sonner-toaster][data-x-position="right"]){right:var(--offset-right)}:where([data-sonner-toaster][data-x-position="left"]){left:var(--offset-left)}:where([data-sonner-toaster][data-x-position="center"]){left:50%;transform:translate(-50%)}:where([data-sonner-toaster][data-y-position="top"]){top:var(--offset-top)}:where([data-sonner-toaster][data-y-position="bottom"]){bottom:var(--offset-bottom)}:where([data-sonner-toast]){--y: translateY(100%);--lift-amount: calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);filter:blur(0);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:none;overflow-wrap:anywhere}:where([data-sonner-toast][data-styled="true"]){padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px #0000001a;width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}:where([data-sonner-toast]:focus-visible){box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}:where([data-sonner-toast][data-y-position="top"]){top:0;--y: translateY(-100%);--lift: 1;--lift-amount: calc(1 * var(--gap))}:where([data-sonner-toast][data-y-position="bottom"]){bottom:0;--y: translateY(100%);--lift: -1;--lift-amount: calc(var(--lift) * var(--gap))}:where([data-sonner-toast]) :where([data-description]){font-weight:400;line-height:1.4;color:inherit}:where([data-sonner-toast]) :where([data-title]){font-weight:500;line-height:1.5;color:inherit}:where([data-sonner-toast]) :where([data-icon]){display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}:where([data-sonner-toast][data-promise="true"]) :where([data-icon])>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}:where([data-sonner-toast]) :where([data-icon])>*{flex-shrink:0}:where([data-sonner-toast]) :where([data-icon]) svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}:where([data-sonner-toast]) :where([data-content]){display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;cursor:pointer;outline:none;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}:where([data-sonner-toast]) :where([data-button]):focus-visible{box-shadow:0 0 0 2px #0006}:where([data-sonner-toast]) :where([data-button]):first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}:where([data-sonner-toast]) :where([data-cancel]){color:var(--normal-text);background:rgba(0,0,0,.08)}:where([data-sonner-toast][data-theme="dark"]) :where([data-cancel]){background:rgba(255,255,255,.3)}:where([data-sonner-toast]) :where([data-close-button]){position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast] [data-close-button]{background:var(--gray1)}:where([data-sonner-toast]) :where([data-close-button]):focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}:where([data-sonner-toast]) :where([data-disabled="true"]){cursor:not-allowed}:where([data-sonner-toast]):hover :where([data-close-button]):hover{background:var(--gray2);border-color:var(--gray5)}:where([data-sonner-toast][data-swiping="true"]):before{content:"";position:absolute;left:-50%;right:-50%;height:100%;z-index:-1}:where([data-sonner-toast][data-y-position="top"][data-swiping="true"]):before{bottom:50%;transform:scaleY(3) translateY(50%)}:where([data-sonner-toast][data-y-position="bottom"][data-swiping="true"]):before{top:50%;transform:scaleY(3) translateY(-50%)}:where([data-sonner-toast][data-swiping="false"][data-removed="true"]):before{content:"";position:absolute;inset:0;transform:scaleY(2)}:where([data-sonner-toast]):after{content:"";position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}:where([data-sonner-toast][data-mounted="true"]){--y: translateY(0);opacity:1}:where([data-sonner-toast][data-expanded="false"][data-front="false"]){--scale: var(--toasts-before) * .05 + 1;--y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}:where([data-sonner-toast])>*{transition:opacity .4s}:where([data-sonner-toast][data-expanded="false"][data-front="false"][data-styled="true"])>*{opacity:0}:where([data-sonner-toast][data-visible="false"]){opacity:0;pointer-events:none}:where([data-sonner-toast][data-mounted="true"][data-expanded="true"]){--y: translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}:where([data-sonner-toast][data-removed="true"][data-front="true"][data-swipe-out="false"]){--y: translateY(calc(var(--lift) * -100%));opacity:0}:where([data-sonner-toast][data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="true"]){--y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));opacity:0}:where([data-sonner-toast][data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="false"]){--y: translateY(40%);opacity:0;transition:transform .5s,opacity .2s}:where([data-sonner-toast][data-removed="true"][data-front="false"]):before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y, 0px)) translate(var(--swipe-amount-x, 0px));transition:none}[data-sonner-toast][data-swiped=true]{user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media (max-width: 600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-theme=light]{--normal-bg: #fff;--normal-border: var(--gray4);--normal-text: var(--gray12);--success-bg: hsl(143, 85%, 96%);--success-border: hsl(145, 92%, 91%);--success-text: hsl(140, 100%, 27%);--info-bg: hsl(208, 100%, 97%);--info-border: hsl(221, 91%, 91%);--info-text: hsl(210, 92%, 45%);--warning-bg: hsl(49, 100%, 97%);--warning-border: hsl(49, 91%, 91%);--warning-text: hsl(31, 92%, 45%);--error-bg: hsl(359, 100%, 97%);--error-border: hsl(359, 100%, 94%);--error-text: hsl(360, 100%, 45%)}[data-sonner-toaster][data-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg: #000;--normal-border: hsl(0, 0%, 20%);--normal-text: var(--gray1)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg: #fff;--normal-border: var(--gray3);--normal-text: var(--gray12)}[data-sonner-toaster][data-theme=dark]{--normal-bg: #000;--normal-bg-hover: hsl(0, 0%, 12%);--normal-border: hsl(0, 0%, 20%);--normal-border-hover: hsl(0, 0%, 25%);--normal-text: var(--gray1);--success-bg: hsl(150, 100%, 6%);--success-border: hsl(147, 100%, 12%);--success-text: hsl(150, 86%, 65%);--info-bg: hsl(215, 100%, 6%);--info-border: hsl(223, 100%, 12%);--info-text: hsl(216, 87%, 65%);--warning-bg: hsl(64, 100%, 6%);--warning-border: hsl(60, 100%, 12%);--warning-text: hsl(46, 87%, 65%);--error-bg: hsl(358, 76%, 10%);--error-border: hsl(357, 89%, 16%);--error-text: hsl(358, 100%, 81%)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success],[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info],[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning],[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error],[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size: 16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:nth-child(1){animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}to{opacity:.15}}@media (prefers-reduced-motion){[data-sonner-toast],[data-sonner-toast]>*,.sonner-loading-bar{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)} `);function Zd(t){return t.label!==void 0}var aD=3,lD="32px",uD="16px",fv=4e3,cD=356,dD=14,fD=20,hD=200;function gi(...t){return t.filter(Boolean).join(" ")}function pD(t){let[e,n]=t.split("-"),r=[];return e&&r.push(e),n&&r.push(n),r}var mD=t=>{var e,n,r,i,s,o,l,c,d,f,p;let{invert:m,toast:g,unstyled:x,interacting:v,setHeights:S,visibleToasts:C,heights:A,index:k,toasts:M,expanded:F,removeToast:I,defaultRichColors:D,closeButton:G,style:X,cancelButtonStyle:P,actionButtonStyle:Y,className:z="",descriptionClassName:ie="",duration:Z,position:ee,gap:ae,loadingIcon:de,expandByDefault:j,classNames:W,icons:O,closeButtonAriaLabel:U="Close toast",pauseWhenPageIsHidden:Q}=t,[R,oe]=we.useState(null),[pe,ue]=we.useState(null),[J,he]=we.useState(!1),[_e,ke]=we.useState(!1),[Ve,ot]=we.useState(!1),[qe,kt]=we.useState(!1),[fn,nt]=we.useState(!1),[Yt,Ct]=we.useState(0),[Pn,Fn]=we.useState(0),on=we.useRef(g.duration||Z||fv),dr=we.useRef(null),Mn=we.useRef(null),Qn=k===0,li=k+1<=C,ce=g.type,ye=g.dismissible!==!1,Qe=g.className||"",ut=g.descriptionClassName||"",rt=we.useMemo(()=>A.findIndex(te=>te.toastId===g.id)||0,[A,g.id]),an=we.useMemo(()=>{var te;return(te=g.closeButton)!=null?te:G},[g.closeButton,G]),Zn=we.useMemo(()=>g.duration||Z||fv,[g.duration,Z]),Re=we.useRef(0),Me=we.useRef(0),Ge=we.useRef(0),Ke=we.useRef(null),[bt,vt]=ee.split("-"),jt=we.useMemo(()=>A.reduce((te,me,De)=>De>=rt?te:te+me.height,0),[A,rt]),fr=JM(),Dt=g.invert||m,$t=ce==="loading";Me.current=we.useMemo(()=>rt*ae+jt,[rt,jt]),we.useEffect(()=>{on.current=Zn},[Zn]),we.useEffect(()=>{he(!0)},[]),we.useEffect(()=>{let te=Mn.current;if(te){let me=te.getBoundingClientRect().height;return Fn(me),S(De=>[{toastId:g.id,height:me,position:g.position},...De]),()=>S(De=>De.filter(wt=>wt.toastId!==g.id))}},[S,g.id]),we.useLayoutEffect(()=>{if(!J)return;let te=Mn.current,me=te.style.height;te.style.height="auto";let De=te.getBoundingClientRect().height;te.style.height=me,Fn(De),S(wt=>wt.find(_t=>_t.toastId===g.id)?wt.map(_t=>_t.toastId===g.id?{..._t,height:De}:_t):[{toastId:g.id,height:De,position:g.position},...wt])},[J,g.title,g.description,S,g.id]);let qt=we.useCallback(()=>{ke(!0),Ct(Me.current),S(te=>te.filter(me=>me.toastId!==g.id)),setTimeout(()=>{I(g)},hD)},[g,I,S,Me]);we.useEffect(()=>{if(g.promise&&ce==="loading"||g.duration===1/0||g.type==="loading")return;let te;return F||v||Q&&fr?(()=>{if(Ge.current{var me;(me=g.onAutoClose)==null||me.call(g,g),qt()},on.current)),()=>clearTimeout(te)},[F,v,g,ce,Q,fr,qt]),we.useEffect(()=>{g.delete&&qt()},[qt,g.delete]);function V(){var te,me,De;return O!=null&&O.loading?we.createElement("div",{className:gi(W?.loader,(te=g?.classNames)==null?void 0:te.loader,"sonner-loader"),"data-visible":ce==="loading"},O.loading):de?we.createElement("div",{className:gi(W?.loader,(me=g?.classNames)==null?void 0:me.loader,"sonner-loader"),"data-visible":ce==="loading"},de):we.createElement(KM,{className:gi(W?.loader,(De=g?.classNames)==null?void 0:De.loader),visible:ce==="loading"})}return we.createElement("li",{tabIndex:0,ref:Mn,className:gi(z,Qe,W?.toast,(e=g?.classNames)==null?void 0:e.toast,W?.default,W?.[ce],(n=g?.classNames)==null?void 0:n[ce]),"data-sonner-toast":"","data-rich-colors":(r=g.richColors)!=null?r:D,"data-styled":!(g.jsx||g.unstyled||x),"data-mounted":J,"data-promise":!!g.promise,"data-swiped":fn,"data-removed":_e,"data-visible":li,"data-y-position":bt,"data-x-position":vt,"data-index":k,"data-front":Qn,"data-swiping":Ve,"data-dismissible":ye,"data-type":ce,"data-invert":Dt,"data-swipe-out":qe,"data-swipe-direction":pe,"data-expanded":!!(F||j&&J),style:{"--index":k,"--toasts-before":k,"--z-index":M.length-k,"--offset":`${_e?Yt:Me.current}px`,"--initial-height":j?"auto":`${Pn}px`,...X,...g.style},onDragEnd:()=>{ot(!1),oe(null),Ke.current=null},onPointerDown:te=>{$t||!ye||(dr.current=new Date,Ct(Me.current),te.target.setPointerCapture(te.pointerId),te.target.tagName!=="BUTTON"&&(ot(!0),Ke.current={x:te.clientX,y:te.clientY}))},onPointerUp:()=>{var te,me,De,wt;if(qe||!ye)return;Ke.current=null;let _t=Number(((te=Mn.current)==null?void 0:te.style.getPropertyValue("--swipe-amount-x").replace("px",""))||0),Oe=Number(((me=Mn.current)==null?void 0:me.style.getPropertyValue("--swipe-amount-y").replace("px",""))||0),Ie=new Date().getTime()-((De=dr.current)==null?void 0:De.getTime()),He=R==="x"?_t:Oe,Bt=Math.abs(He)/Ie;if(Math.abs(He)>=fD||Bt>.11){Ct(Me.current),(wt=g.onDismiss)==null||wt.call(g,g),ue(R==="x"?_t>0?"right":"left":Oe>0?"down":"up"),qt(),kt(!0),nt(!1);return}ot(!1),oe(null)},onPointerMove:te=>{var me,De,wt,_t;if(!Ke.current||!ye||((me=window.getSelection())==null?void 0:me.toString().length)>0)return;let Oe=te.clientY-Ke.current.y,Ie=te.clientX-Ke.current.x,He=(De=t.swipeDirections)!=null?De:pD(ee);!R&&(Math.abs(Ie)>1||Math.abs(Oe)>1)&&oe(Math.abs(Ie)>Math.abs(Oe)?"x":"y");let Bt={x:0,y:0};R==="y"?(He.includes("top")||He.includes("bottom"))&&(He.includes("top")&&Oe<0||He.includes("bottom")&&Oe>0)&&(Bt.y=Oe):R==="x"&&(He.includes("left")||He.includes("right"))&&(He.includes("left")&&Ie<0||He.includes("right")&&Ie>0)&&(Bt.x=Ie),(Math.abs(Bt.x)>0||Math.abs(Bt.y)>0)&&nt(!0),(wt=Mn.current)==null||wt.style.setProperty("--swipe-amount-x",`${Bt.x}px`),(_t=Mn.current)==null||_t.style.setProperty("--swipe-amount-y",`${Bt.y}px`)}},an&&!g.jsx?we.createElement("button",{"aria-label":U,"data-disabled":$t,"data-close-button":!0,onClick:$t||!ye?()=>{}:()=>{var te;qt(),(te=g.onDismiss)==null||te.call(g,g)},className:gi(W?.closeButton,(i=g?.classNames)==null?void 0:i.closeButton)},(s=O?.close)!=null?s:ZM):null,g.jsx||T.isValidElement(g.title)?g.jsx?g.jsx:typeof g.title=="function"?g.title():g.title:we.createElement(we.Fragment,null,ce||g.icon||g.promise?we.createElement("div",{"data-icon":"",className:gi(W?.icon,(o=g?.classNames)==null?void 0:o.icon)},g.promise||g.type==="loading"&&!g.icon?g.icon||V():null,g.type!=="loading"?g.icon||O?.[ce]||VM(ce):null):null,we.createElement("div",{"data-content":"",className:gi(W?.content,(l=g?.classNames)==null?void 0:l.content)},we.createElement("div",{"data-title":"",className:gi(W?.title,(c=g?.classNames)==null?void 0:c.title)},typeof g.title=="function"?g.title():g.title),g.description?we.createElement("div",{"data-description":"",className:gi(ie,ut,W?.description,(d=g?.classNames)==null?void 0:d.description)},typeof g.description=="function"?g.description():g.description):null),T.isValidElement(g.cancel)?g.cancel:g.cancel&&Zd(g.cancel)?we.createElement("button",{"data-button":!0,"data-cancel":!0,style:g.cancelButtonStyle||P,onClick:te=>{var me,De;Zd(g.cancel)&&ye&&((De=(me=g.cancel).onClick)==null||De.call(me,te),qt())},className:gi(W?.cancelButton,(f=g?.classNames)==null?void 0:f.cancelButton)},g.cancel.label):null,T.isValidElement(g.action)?g.action:g.action&&Zd(g.action)?we.createElement("button",{"data-button":!0,"data-action":!0,style:g.actionButtonStyle||Y,onClick:te=>{var me,De;Zd(g.action)&&((De=(me=g.action).onClick)==null||De.call(me,te),!te.defaultPrevented&&qt())},className:gi(W?.actionButton,(p=g?.classNames)==null?void 0:p.actionButton)},g.action.label):null))};function hv(){if(typeof window>"u"||typeof document>"u")return"ltr";let t=document.documentElement.getAttribute("dir");return t==="auto"||!t?window.getComputedStyle(document.documentElement).direction:t}function gD(t,e){let n={};return[t,e].forEach((r,i)=>{let s=i===1,o=s?"--mobile-offset":"--offset",l=s?uD:lD;function c(d){["top","right","bottom","left"].forEach(f=>{n[`${o}-${f}`]=typeof d=="number"?`${d}px`:d})}typeof r=="number"||typeof r=="string"?c(r):typeof r=="object"?["top","right","bottom","left"].forEach(d=>{r[d]===void 0?n[`${o}-${d}`]=l:n[`${o}-${d}`]=typeof r[d]=="number"?`${r[d]}px`:r[d]}):c(l)}),n}var bD=T.forwardRef(function(t,e){let{invert:n,position:r="bottom-right",hotkey:i=["altKey","KeyT"],expand:s,closeButton:o,className:l,offset:c,mobileOffset:d,theme:f="light",richColors:p,duration:m,style:g,visibleToasts:x=aD,toastOptions:v,dir:S=hv(),gap:C=dD,loadingIcon:A,icons:k,containerAriaLabel:M="Notifications",pauseWhenPageIsHidden:F}=t,[I,D]=we.useState([]),G=we.useMemo(()=>Array.from(new Set([r].concat(I.filter(Q=>Q.position).map(Q=>Q.position)))),[I,r]),[X,P]=we.useState([]),[Y,z]=we.useState(!1),[ie,Z]=we.useState(!1),[ee,ae]=we.useState(f!=="system"?f:typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"),de=we.useRef(null),j=i.join("+").replace(/Key/g,"").replace(/Digit/g,""),W=we.useRef(null),O=we.useRef(!1),U=we.useCallback(Q=>{D(R=>{var oe;return(oe=R.find(pe=>pe.id===Q.id))!=null&&oe.delete||Rr.dismiss(Q.id),R.filter(({id:pe})=>pe!==Q.id)})},[]);return we.useEffect(()=>Rr.subscribe(Q=>{if(Q.dismiss){D(R=>R.map(oe=>oe.id===Q.id?{...oe,delete:!0}:oe));return}setTimeout(()=>{e_.flushSync(()=>{D(R=>{let oe=R.findIndex(pe=>pe.id===Q.id);return oe!==-1?[...R.slice(0,oe),{...R[oe],...Q},...R.slice(oe+1)]:[Q,...R]})})})}),[]),we.useEffect(()=>{if(f!=="system"){ae(f);return}if(f==="system"&&(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?ae("dark"):ae("light")),typeof window>"u")return;let Q=window.matchMedia("(prefers-color-scheme: dark)");try{Q.addEventListener("change",({matches:R})=>{ae(R?"dark":"light")})}catch{Q.addListener(({matches:oe})=>{try{ae(oe?"dark":"light")}catch(pe){console.error(pe)}})}},[f]),we.useEffect(()=>{I.length<=1&&z(!1)},[I]),we.useEffect(()=>{let Q=R=>{var oe,pe;i.every(ue=>R[ue]||R.code===ue)&&(z(!0),(oe=de.current)==null||oe.focus()),R.code==="Escape"&&(document.activeElement===de.current||(pe=de.current)!=null&&pe.contains(document.activeElement))&&z(!1)};return document.addEventListener("keydown",Q),()=>document.removeEventListener("keydown",Q)},[i]),we.useEffect(()=>{if(de.current)return()=>{W.current&&(W.current.focus({preventScroll:!0}),W.current=null,O.current=!1)}},[de.current]),we.createElement("section",{ref:e,"aria-label":`${M} ${j}`,tabIndex:-1,"aria-live":"polite","aria-relevant":"additions text","aria-atomic":"false",suppressHydrationWarning:!0},G.map((Q,R)=>{var oe;let[pe,ue]=Q.split("-");return I.length?we.createElement("ol",{key:Q,dir:S==="auto"?hv():S,tabIndex:-1,ref:de,className:l,"data-sonner-toaster":!0,"data-theme":ee,"data-y-position":pe,"data-lifted":Y&&I.length>1&&!s,"data-x-position":ue,style:{"--front-toast-height":`${((oe=X[0])==null?void 0:oe.height)||0}px`,"--width":`${cD}px`,"--gap":`${C}px`,...g,...gD(c,d)},onBlur:J=>{O.current&&!J.currentTarget.contains(J.relatedTarget)&&(O.current=!1,W.current&&(W.current.focus({preventScroll:!0}),W.current=null))},onFocus:J=>{J.target instanceof HTMLElement&&J.target.dataset.dismissible==="false"||O.current||(O.current=!0,W.current=J.relatedTarget)},onMouseEnter:()=>z(!0),onMouseMove:()=>z(!0),onMouseLeave:()=>{ie||z(!1)},onDragEnd:()=>z(!1),onPointerDown:J=>{J.target instanceof HTMLElement&&J.target.dataset.dismissible==="false"||Z(!0)},onPointerUp:()=>Z(!1)},I.filter(J=>!J.position&&R===0||J.position===Q).map((J,he)=>{var _e,ke;return we.createElement(mD,{key:J.id,icons:k,index:he,toast:J,defaultRichColors:p,duration:(_e=v?.duration)!=null?_e:m,className:v?.className,descriptionClassName:v?.descriptionClassName,invert:n,visibleToasts:x,closeButton:(ke=v?.closeButton)!=null?ke:o,interacting:ie,position:Q,style:v?.style,unstyled:v?.unstyled,classNames:v?.classNames,cancelButtonStyle:v?.cancelButtonStyle,actionButtonStyle:v?.actionButtonStyle,removeToast:U,toasts:I.filter(Ve=>Ve.position==J.position),heights:X.filter(Ve=>Ve.position==J.position),setHeights:P,expandByDefault:s,gap:C,loadingIcon:A,expanded:Y,pauseWhenPageIsHidden:F,swipeDirections:t.swipeDirections})})):null}))});const pv=20,Jd=404,mv="Error: Gateway Timeout";function hg(t,e,n=[],r=!1,i=!0,s=!0){const[o,l]=T.useState(null),[c,d]=T.useState(!1),[f,p]=T.useState(null),[m,g]=T.useState(!1),x="",v=T.useRef(null);T.useEffect(()=>{if(f&&f.message!==mv)throw new Error(`Failed to fetch "${t}"`)},[f,t]);const S=()=>w.jsxs("div",{children:[w.jsx("div",{children:"Something went wrong"}),w.jsx("div",{role:"button",onClick:()=>g(M=>!M),className:"underline cursor-pointer",children:"Click to retry"})]}),C=()=>g(M=>!M);T.useEffect(()=>{r&&f?.message===mv&&(g(M=>!M),f.message="")},[r,f]);const A=T.useCallback((M="")=>{const F=new AbortController;v.current=F;const{signal:I}=F;setTimeout(()=>d(!0),0),p(null),fetch(`${lo}/${t}${M||x}`,{signal:I}).then(async G=>{if(!G.ok)throw G.status===Jd?{code:Jd,message:G.statusText}:new Error(`Error: ${G.statusText}`);const X=await G.json();l(X)}).catch(G=>{s&&G.code!==pv?p({message:G.message}):G.code!==pv&&G.code!==Jd&&r&&A(),G.code===Jd&&Ir.error("resource not found. please try to refresh the page")}).finally(()=>s&&d(!1))},[t,m,...n]);return T.useEffect(()=>{if(i)return A(),()=>{v.current?.abort()}},[A,i]),{data:o,loading:c,error:f,refetch:C,ErrorTemplate:f&&S,abortFetch:()=>{v.current?.abort()}}}function t_(t){var e,n,r="";if(typeof t=="string"||typeof t=="number")r+=t;else if(typeof t=="object")if(Array.isArray(t)){var i=t.length;for(e=0;e{const e=xD(t),{conflictingClassGroups:n,conflictingClassGroupModifiers:r}=t;return{getClassGroupId:o=>{const l=o.split(Jb);return l[0]===""&&l.length!==1&&l.shift(),n_(l,e)||yD(o)},getConflictingClassGroupIds:(o,l)=>{const c=n[o]||[];return l&&r[o]?[...c,...r[o]]:c}}},n_=(t,e)=>{if(t.length===0)return e.classGroupId;const n=t[0],r=e.nextPart.get(n),i=r?n_(t.slice(1),r):void 0;if(i)return i;if(e.validators.length===0)return;const s=t.join(Jb);return e.validators.find(({validator:o})=>o(s))?.classGroupId},gv=/^\[(.+)\]$/,yD=t=>{if(gv.test(t)){const e=gv.exec(t)[1],n=e?.substring(0,e.indexOf(":"));if(n)return"arbitrary.."+n}},xD=t=>{const{theme:e,prefix:n}=t,r={nextPart:new Map,validators:[]};return wD(Object.entries(t.classGroups),n).forEach(([s,o])=>{A0(o,r,s,e)}),r},A0=(t,e,n,r)=>{t.forEach(i=>{if(typeof i=="string"){const s=i===""?e:bv(e,i);s.classGroupId=n;return}if(typeof i=="function"){if(vD(i)){A0(i(r),e,n,r);return}e.validators.push({validator:i,classGroupId:n});return}Object.entries(i).forEach(([s,o])=>{A0(o,bv(e,s),n,r)})})},bv=(t,e)=>{let n=t;return e.split(Jb).forEach(r=>{n.nextPart.has(r)||n.nextPart.set(r,{nextPart:new Map,validators:[]}),n=n.nextPart.get(r)}),n},vD=t=>t.isThemeGetter,wD=(t,e)=>e?t.map(([n,r])=>{const i=r.map(s=>typeof s=="string"?e+s:typeof s=="object"?Object.fromEntries(Object.entries(s).map(([o,l])=>[e+o,l])):s);return[n,i]}):t,TD=t=>{if(t<1)return{get:()=>{},set:()=>{}};let e=0,n=new Map,r=new Map;const i=(s,o)=>{n.set(s,o),e++,e>t&&(e=0,r=n,n=new Map)};return{get(s){let o=n.get(s);if(o!==void 0)return o;if((o=r.get(s))!==void 0)return i(s,o),o},set(s,o){n.has(s)?n.set(s,o):i(s,o)}}},r_="!",SD=t=>{const{separator:e,experimentalParseClassName:n}=t,r=e.length===1,i=e[0],s=e.length,o=l=>{const c=[];let d=0,f=0,p;for(let S=0;Sf?p-f:void 0;return{modifiers:c,hasImportantModifier:g,baseClassName:x,maybePostfixModifierPosition:v}};return n?l=>n({className:l,parseClassName:o}):o},_D=t=>{if(t.length<=1)return t;const e=[];let n=[];return t.forEach(r=>{r[0]==="["?(e.push(...n.sort(),r),n=[]):n.push(r)}),e.push(...n.sort()),e},CD=t=>({cache:TD(t.cacheSize),parseClassName:SD(t),...ED(t)}),AD=/\s+/,kD=(t,e)=>{const{parseClassName:n,getClassGroupId:r,getConflictingClassGroupIds:i}=e,s=[],o=t.trim().split(AD);let l="";for(let c=o.length-1;c>=0;c-=1){const d=o[c],{modifiers:f,hasImportantModifier:p,baseClassName:m,maybePostfixModifierPosition:g}=n(d);let x=!!g,v=r(x?m.substring(0,g):m);if(!v){if(!x){l=d+(l.length>0?" "+l:l);continue}if(v=r(m),!v){l=d+(l.length>0?" "+l:l);continue}x=!1}const S=_D(f).join(":"),C=p?S+r_:S,A=C+v;if(s.includes(A))continue;s.push(A);const k=i(v,x);for(let M=0;M0?" "+l:l)}return l};function On(){let t=0,e,n,r="";for(;t{if(typeof t=="string")return t;let e,n="";for(let r=0;rp(f),t());return n=CD(d),r=n.cache.get,i=n.cache.set,s=l,l(c)}function l(c){const d=r(c);if(d)return d;const f=kD(c,n);return i(c,f),f}return function(){return s(On.apply(null,arguments))}}const rn=t=>{const e=n=>n[t]||[];return e.isThemeGetter=!0,e},s_=/^\[(?:([a-z-]+):)?(.+)\]$/i,RD=/^\d+\/\d+$/,ID=new Set(["px","full","screen"]),OD=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,MD=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,DD=/^(rgba?|hsla?|hwb|(ok)?(lab|lch))\(.+\)$/,LD=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,PD=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,cs=t=>nl(t)||ID.has(t)||RD.test(t),qs=t=>kl(t,"length",WD),nl=t=>!!t&&!Number.isNaN(Number(t)),pg=t=>kl(t,"number",nl),Tu=t=>!!t&&Number.isInteger(Number(t)),FD=t=>t.endsWith("%")&&nl(t.slice(0,-1)),ht=t=>s_.test(t),Xs=t=>OD.test(t),BD=new Set(["length","size","percentage"]),UD=t=>kl(t,BD,o_),HD=t=>kl(t,"position",o_),zD=new Set(["image","url"]),jD=t=>kl(t,zD,GD),$D=t=>kl(t,"",VD),Su=()=>!0,kl=(t,e,n)=>{const r=s_.exec(t);return r?r[1]?typeof e=="string"?r[1]===e:e.has(r[1]):n(r[2]):!1},WD=t=>MD.test(t)&&!DD.test(t),o_=()=>!1,VD=t=>LD.test(t),GD=t=>PD.test(t),KD=()=>{const t=rn("colors"),e=rn("spacing"),n=rn("blur"),r=rn("brightness"),i=rn("borderColor"),s=rn("borderRadius"),o=rn("borderSpacing"),l=rn("borderWidth"),c=rn("contrast"),d=rn("grayscale"),f=rn("hueRotate"),p=rn("invert"),m=rn("gap"),g=rn("gradientColorStops"),x=rn("gradientColorStopPositions"),v=rn("inset"),S=rn("margin"),C=rn("opacity"),A=rn("padding"),k=rn("saturate"),M=rn("scale"),F=rn("sepia"),I=rn("skew"),D=rn("space"),G=rn("translate"),X=()=>["auto","contain","none"],P=()=>["auto","hidden","clip","visible","scroll"],Y=()=>["auto",ht,e],z=()=>[ht,e],ie=()=>["",cs,qs],Z=()=>["auto",nl,ht],ee=()=>["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top"],ae=()=>["solid","dashed","dotted","double","none"],de=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],j=()=>["start","end","center","between","around","evenly","stretch"],W=()=>["","0",ht],O=()=>["auto","avoid","all","avoid-page","page","left","right","column"],U=()=>[nl,ht];return{cacheSize:500,separator:":",theme:{colors:[Su],spacing:[cs,qs],blur:["none","",Xs,ht],brightness:U(),borderColor:[t],borderRadius:["none","","full",Xs,ht],borderSpacing:z(),borderWidth:ie(),contrast:U(),grayscale:W(),hueRotate:U(),invert:W(),gap:z(),gradientColorStops:[t],gradientColorStopPositions:[FD,qs],inset:Y(),margin:Y(),opacity:U(),padding:z(),saturate:U(),scale:U(),sepia:W(),skew:U(),space:z(),translate:z()},classGroups:{aspect:[{aspect:["auto","square","video",ht]}],container:["container"],columns:[{columns:[Xs]}],"break-after":[{"break-after":O()}],"break-before":[{"break-before":O()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:[...ee(),ht]}],overflow:[{overflow:P()}],"overflow-x":[{"overflow-x":P()}],"overflow-y":[{"overflow-y":P()}],overscroll:[{overscroll:X()}],"overscroll-x":[{"overscroll-x":X()}],"overscroll-y":[{"overscroll-y":X()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:[v]}],"inset-x":[{"inset-x":[v]}],"inset-y":[{"inset-y":[v]}],start:[{start:[v]}],end:[{end:[v]}],top:[{top:[v]}],right:[{right:[v]}],bottom:[{bottom:[v]}],left:[{left:[v]}],visibility:["visible","invisible","collapse"],z:[{z:["auto",Tu,ht]}],basis:[{basis:Y()}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["wrap","wrap-reverse","nowrap"]}],flex:[{flex:["1","auto","initial","none",ht]}],grow:[{grow:W()}],shrink:[{shrink:W()}],order:[{order:["first","last","none",Tu,ht]}],"grid-cols":[{"grid-cols":[Su]}],"col-start-end":[{col:["auto",{span:["full",Tu,ht]},ht]}],"col-start":[{"col-start":Z()}],"col-end":[{"col-end":Z()}],"grid-rows":[{"grid-rows":[Su]}],"row-start-end":[{row:["auto",{span:[Tu,ht]},ht]}],"row-start":[{"row-start":Z()}],"row-end":[{"row-end":Z()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":["auto","min","max","fr",ht]}],"auto-rows":[{"auto-rows":["auto","min","max","fr",ht]}],gap:[{gap:[m]}],"gap-x":[{"gap-x":[m]}],"gap-y":[{"gap-y":[m]}],"justify-content":[{justify:["normal",...j()]}],"justify-items":[{"justify-items":["start","end","center","stretch"]}],"justify-self":[{"justify-self":["auto","start","end","center","stretch"]}],"align-content":[{content:["normal",...j(),"baseline"]}],"align-items":[{items:["start","end","center","baseline","stretch"]}],"align-self":[{self:["auto","start","end","center","stretch","baseline"]}],"place-content":[{"place-content":[...j(),"baseline"]}],"place-items":[{"place-items":["start","end","center","baseline","stretch"]}],"place-self":[{"place-self":["auto","start","end","center","stretch"]}],p:[{p:[A]}],px:[{px:[A]}],py:[{py:[A]}],ps:[{ps:[A]}],pe:[{pe:[A]}],pt:[{pt:[A]}],pr:[{pr:[A]}],pb:[{pb:[A]}],pl:[{pl:[A]}],m:[{m:[S]}],mx:[{mx:[S]}],my:[{my:[S]}],ms:[{ms:[S]}],me:[{me:[S]}],mt:[{mt:[S]}],mr:[{mr:[S]}],mb:[{mb:[S]}],ml:[{ml:[S]}],"space-x":[{"space-x":[D]}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":[D]}],"space-y-reverse":["space-y-reverse"],w:[{w:["auto","min","max","fit","svw","lvw","dvw",ht,e]}],"min-w":[{"min-w":[ht,e,"min","max","fit"]}],"max-w":[{"max-w":[ht,e,"none","full","min","max","fit","prose",{screen:[Xs]},Xs]}],h:[{h:[ht,e,"auto","min","max","fit","svh","lvh","dvh"]}],"min-h":[{"min-h":[ht,e,"min","max","fit","svh","lvh","dvh"]}],"max-h":[{"max-h":[ht,e,"min","max","fit","svh","lvh","dvh"]}],size:[{size:[ht,e,"auto","min","max","fit"]}],"font-size":[{text:["base",Xs,qs]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:["thin","extralight","light","normal","medium","semibold","bold","extrabold","black",pg]}],"font-family":[{font:[Su]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:["tighter","tight","normal","wide","wider","widest",ht]}],"line-clamp":[{"line-clamp":["none",nl,pg]}],leading:[{leading:["none","tight","snug","normal","relaxed","loose",cs,ht]}],"list-image":[{"list-image":["none",ht]}],"list-style-type":[{list:["none","disc","decimal",ht]}],"list-style-position":[{list:["inside","outside"]}],"placeholder-color":[{placeholder:[t]}],"placeholder-opacity":[{"placeholder-opacity":[C]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"text-color":[{text:[t]}],"text-opacity":[{"text-opacity":[C]}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...ae(),"wavy"]}],"text-decoration-thickness":[{decoration:["auto","from-font",cs,qs]}],"underline-offset":[{"underline-offset":["auto",cs,ht]}],"text-decoration-color":[{decoration:[t]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:z()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",ht]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",ht]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-opacity":[{"bg-opacity":[C]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:[...ee(),HD]}],"bg-repeat":[{bg:["no-repeat",{repeat:["","x","y","round","space"]}]}],"bg-size":[{bg:["auto","cover","contain",UD]}],"bg-image":[{bg:["none",{"gradient-to":["t","tr","r","br","b","bl","l","tl"]},jD]}],"bg-color":[{bg:[t]}],"gradient-from-pos":[{from:[x]}],"gradient-via-pos":[{via:[x]}],"gradient-to-pos":[{to:[x]}],"gradient-from":[{from:[g]}],"gradient-via":[{via:[g]}],"gradient-to":[{to:[g]}],rounded:[{rounded:[s]}],"rounded-s":[{"rounded-s":[s]}],"rounded-e":[{"rounded-e":[s]}],"rounded-t":[{"rounded-t":[s]}],"rounded-r":[{"rounded-r":[s]}],"rounded-b":[{"rounded-b":[s]}],"rounded-l":[{"rounded-l":[s]}],"rounded-ss":[{"rounded-ss":[s]}],"rounded-se":[{"rounded-se":[s]}],"rounded-ee":[{"rounded-ee":[s]}],"rounded-es":[{"rounded-es":[s]}],"rounded-tl":[{"rounded-tl":[s]}],"rounded-tr":[{"rounded-tr":[s]}],"rounded-br":[{"rounded-br":[s]}],"rounded-bl":[{"rounded-bl":[s]}],"border-w":[{border:[l]}],"border-w-x":[{"border-x":[l]}],"border-w-y":[{"border-y":[l]}],"border-w-s":[{"border-s":[l]}],"border-w-e":[{"border-e":[l]}],"border-w-t":[{"border-t":[l]}],"border-w-r":[{"border-r":[l]}],"border-w-b":[{"border-b":[l]}],"border-w-l":[{"border-l":[l]}],"border-opacity":[{"border-opacity":[C]}],"border-style":[{border:[...ae(),"hidden"]}],"divide-x":[{"divide-x":[l]}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":[l]}],"divide-y-reverse":["divide-y-reverse"],"divide-opacity":[{"divide-opacity":[C]}],"divide-style":[{divide:ae()}],"border-color":[{border:[i]}],"border-color-x":[{"border-x":[i]}],"border-color-y":[{"border-y":[i]}],"border-color-s":[{"border-s":[i]}],"border-color-e":[{"border-e":[i]}],"border-color-t":[{"border-t":[i]}],"border-color-r":[{"border-r":[i]}],"border-color-b":[{"border-b":[i]}],"border-color-l":[{"border-l":[i]}],"divide-color":[{divide:[i]}],"outline-style":[{outline:["",...ae()]}],"outline-offset":[{"outline-offset":[cs,ht]}],"outline-w":[{outline:[cs,qs]}],"outline-color":[{outline:[t]}],"ring-w":[{ring:ie()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:[t]}],"ring-opacity":[{"ring-opacity":[C]}],"ring-offset-w":[{"ring-offset":[cs,qs]}],"ring-offset-color":[{"ring-offset":[t]}],shadow:[{shadow:["","inner","none",Xs,$D]}],"shadow-color":[{shadow:[Su]}],opacity:[{opacity:[C]}],"mix-blend":[{"mix-blend":[...de(),"plus-lighter","plus-darker"]}],"bg-blend":[{"bg-blend":de()}],filter:[{filter:["","none"]}],blur:[{blur:[n]}],brightness:[{brightness:[r]}],contrast:[{contrast:[c]}],"drop-shadow":[{"drop-shadow":["","none",Xs,ht]}],grayscale:[{grayscale:[d]}],"hue-rotate":[{"hue-rotate":[f]}],invert:[{invert:[p]}],saturate:[{saturate:[k]}],sepia:[{sepia:[F]}],"backdrop-filter":[{"backdrop-filter":["","none"]}],"backdrop-blur":[{"backdrop-blur":[n]}],"backdrop-brightness":[{"backdrop-brightness":[r]}],"backdrop-contrast":[{"backdrop-contrast":[c]}],"backdrop-grayscale":[{"backdrop-grayscale":[d]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[f]}],"backdrop-invert":[{"backdrop-invert":[p]}],"backdrop-opacity":[{"backdrop-opacity":[C]}],"backdrop-saturate":[{"backdrop-saturate":[k]}],"backdrop-sepia":[{"backdrop-sepia":[F]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":[o]}],"border-spacing-x":[{"border-spacing-x":[o]}],"border-spacing-y":[{"border-spacing-y":[o]}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["none","all","","colors","opacity","shadow","transform",ht]}],duration:[{duration:U()}],ease:[{ease:["linear","in","out","in-out",ht]}],delay:[{delay:U()}],animate:[{animate:["none","spin","ping","pulse","bounce",ht]}],transform:[{transform:["","gpu","none"]}],scale:[{scale:[M]}],"scale-x":[{"scale-x":[M]}],"scale-y":[{"scale-y":[M]}],rotate:[{rotate:[Tu,ht]}],"translate-x":[{"translate-x":[G]}],"translate-y":[{"translate-y":[G]}],"skew-x":[{"skew-x":[I]}],"skew-y":[{"skew-y":[I]}],"transform-origin":[{origin:["center","top","top-right","right","bottom-right","bottom","bottom-left","left","top-left",ht]}],accent:[{accent:["auto",t]}],appearance:[{appearance:["none","auto"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",ht]}],"caret-color":[{caret:[t]}],"pointer-events":[{"pointer-events":["none","auto"]}],resize:[{resize:["none","y","x",""]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":z()}],"scroll-mx":[{"scroll-mx":z()}],"scroll-my":[{"scroll-my":z()}],"scroll-ms":[{"scroll-ms":z()}],"scroll-me":[{"scroll-me":z()}],"scroll-mt":[{"scroll-mt":z()}],"scroll-mr":[{"scroll-mr":z()}],"scroll-mb":[{"scroll-mb":z()}],"scroll-ml":[{"scroll-ml":z()}],"scroll-p":[{"scroll-p":z()}],"scroll-px":[{"scroll-px":z()}],"scroll-py":[{"scroll-py":z()}],"scroll-ps":[{"scroll-ps":z()}],"scroll-pe":[{"scroll-pe":z()}],"scroll-pt":[{"scroll-pt":z()}],"scroll-pr":[{"scroll-pr":z()}],"scroll-pb":[{"scroll-pb":z()}],"scroll-pl":[{"scroll-pl":z()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",ht]}],fill:[{fill:[t,"none"]}],"stroke-w":[{stroke:[cs,qs,pg]}],stroke:[{stroke:[t,"none"]}],sr:["sr-only","not-sr-only"],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]}}},Xe=ND(KD),qf=new BroadcastChannel("active_tabs"),eE=Date.now()+"-"+Math.random();console.log("broadcasting...");qf.postMessage({type:"opened",timestamp:Date.now(),id:eE});window.addEventListener("beforeunload",()=>{qf.postMessage({tabId:eE,type:"closed"}),sessionStorage.setItem("active_tabs",JSON.stringify([])),qf.close()});qf.onmessage=t=>{const e=JSON.parse(sessionStorage.getItem("active_tabs")||"[]");if(t.data.type==="opened"&&t.data.id!==eE)sessionStorage.setItem("active_tabs",JSON.stringify([...e,t.data.id]));else if(t.data.type==="closed"){console.log("closedddd");const n=e.filter(r=>r!==t.data.tabId);sessionStorage.setItem("active_tabs",JSON.stringify(n))}console.log("Message from another tab:",t.data,t)};const YD=()=>JSON.parse(sessionStorage.getItem("active_tabs")||"[]").length;function Rt(...t){return Xe(Al(t))}const Ev=(t,e)=>t?new Date(t).toLocaleDateString()===new Date(e).toLocaleDateString():!1,hl=(t,e)=>{navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(t).then(()=>Ir.info(t?.length<100?`Copied text: ${t}`:"Text copied")).catch(()=>{Xf(t,e)}):Xf(t,e)},Xf=(t,e)=>{const n=document.createElement("textarea");n.value=t,(e||document.body).appendChild(n),n.style.position="fixed",n.select();try{document.execCommand("copy")?Ir.info(t?.length<100?`Copied text: ${t}`:"Text copied"):console.error("Fallback: Copy command failed.")}catch(r){console.error("Fallback: Unable to copy",r)}finally{(e||document.body).removeChild(n)}},qD=(t,e,n={})=>{try{const{headers:r=[],delimiter:i=",",includeHeaders:s=!0,dateFormat:o="iso"}=n;if(!t||t.length===0)throw new Error("No data to export");const l=r.length>0?r:Object.keys(t[0]),c=x=>{const v=String(x||"");return v.includes(i)||v.includes('"')||v.includes(` `)?`"${v.replace(/"/g,'""')}"`:v},d=x=>x instanceof Date?o==="readable"?x.toLocaleString():x.toISOString():x,f=[];s&&f.push(l.map(x=>c(x)).join(i)),t.forEach(x=>{const v=l.map(S=>{const C=x[S];return c(d(C))});f.push(v.join(i))});const p=f.join(` `),m=new Blob([p],{type:"text/csv;charset=utf-8;"}),g=document.createElement("a");if(g.download!==void 0){const x=URL.createObjectURL(m);return g.setAttribute("href",x),g.setAttribute("download",e),g.style.visibility="hidden",document.body.appendChild(g),g.click(),document.body.removeChild(g),URL.revokeObjectURL(x),!0}return!1}catch(r){throw console.error("CSV export failed:",r),r}};function Lh(t,e,n){return new Promise((r,i)=>{const s=indexedDB.open(t,1);s.onupgradeneeded=()=>{const o=s.result;if(!o.objectStoreNames.contains(e)){const l=o.createObjectStore(e,{autoIncrement:!0});n&&l.createIndex(n.name,n.keyPath,{unique:!1})}},s.onsuccess=()=>r(s.result),s.onerror=()=>i(s.error)})}const XD=async(t,e,n,r,i="update",s)=>{const c=(await Lh(t,e,s)).transaction(e,"readwrite").objectStore(e);if(i==="multiple"){const d=c.get(n);d.onsuccess=()=>{let f=d.result;Array.isArray(f)||(f=f!==void 0?[f]:[]),f.push(r);const p=c.put(f,n);p.onsuccess=()=>{console.log("Item appended in IndexedDB")},p.onerror=()=>{console.error("Error appending item in IndexedDB")}},d.onerror=()=>{console.error("Error getting item for multiple mode in IndexedDB")}}else{const d=c.put(r,n);d.onerror=()=>{console.error("Error updating item in IndexedDB")}}},QD=async(t,e,n)=>{const o=(await Lh(t,e)).transaction(e,"readwrite").objectStore(e).delete(n);o.onerror=()=>{console.error("Error deleting item in IndexedDB")}},ZD=async(t,e,n,r)=>{try{const o=(await Lh(t,e,r)).transaction(e,"readonly").objectStore(e);return await new Promise((c,d)=>{const f=o.get(n);f.onsuccess=()=>c(f.result),f.onerror=()=>d(f.error)})}catch(i){return console.error("Error opening IndexedDB:",i),null}},a_=async(t,e,n,r,i,s)=>{try{const d=(await Lh(t,e,i)).transaction(e,"readonly").objectStore(e).index(n),f=await new Promise((p,m)=>{const g=d.getAll(r);g.onsuccess=()=>p(g.result),g.onerror=()=>m(g.error)});return s?f.reduce((p,m)=>(p[m.traceId]=m.flagValue,p),{}):f}catch(o){return console.error("Error opening IndexedDB:",o),null}},sc=T.forwardRef(({className:t,type:e,...n},r)=>w.jsx("input",{type:e,className:Rt("flex h-10 w-full rounded-md border border-[#eeeeee] bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",t),ref:r,...n}));sc.displayName="Input";function je(t,e,{checkForDefaultPrevented:n=!0}={}){return function(i){if(t?.(i),n===!1||!i.defaultPrevented)return e?.(i)}}function yv(t,e){if(typeof t=="function")return t(e);t!=null&&(t.current=e)}function Ph(...t){return e=>{let n=!1;const r=t.map(i=>{const s=yv(i,e);return!n&&typeof s=="function"&&(n=!0),s});if(n)return()=>{for(let i=0;i{const{children:o,...l}=s,c=T.useMemo(()=>l,Object.values(l));return w.jsx(n.Provider,{value:c,children:o})};r.displayName=t+"Provider";function i(s){const o=T.useContext(n);if(o)return o;if(e!==void 0)return e;throw new Error(`\`${s}\` must be used within \`${t}\``)}return[r,i]}function Cs(t,e=[]){let n=[];function r(s,o){const l=T.createContext(o),c=n.length;n=[...n,o];const d=p=>{const{scope:m,children:g,...x}=p,v=m?.[t]?.[c]||l,S=T.useMemo(()=>x,Object.values(x));return w.jsx(v.Provider,{value:S,children:g})};d.displayName=s+"Provider";function f(p,m){const g=m?.[t]?.[c]||l,x=T.useContext(g);if(x)return x;if(o!==void 0)return o;throw new Error(`\`${p}\` must be used within \`${s}\``)}return[d,f]}const i=()=>{const s=n.map(o=>T.createContext(o));return function(l){const c=l?.[t]||s;return T.useMemo(()=>({[`__scope${t}`]:{...l,[t]:c}}),[l,c])}};return i.scopeName=t,[r,eL(i,...e)]}function eL(...t){const e=t[0];if(t.length===1)return e;const n=()=>{const r=t.map(i=>({useScope:i(),scopeName:i.scopeName}));return function(s){const o=r.reduce((l,{useScope:c,scopeName:d})=>{const p=c(s)[`__scope${d}`];return{...l,...p}},{});return T.useMemo(()=>({[`__scope${e.scopeName}`]:o}),[o])}};return n.scopeName=e.scopeName,n}function Go(t){const e=nL(t),n=T.forwardRef((r,i)=>{const{children:s,...o}=r,l=T.Children.toArray(s),c=l.find(iL);if(c){const d=c.props.children,f=l.map(p=>p===c?T.Children.count(d)>1?T.Children.only(null):T.isValidElement(d)?d.props.children:null:p);return w.jsx(e,{...o,ref:i,children:T.isValidElement(d)?T.cloneElement(d,void 0,f):null})}return w.jsx(e,{...o,ref:i,children:s})});return n.displayName=`${t}.Slot`,n}var tL=Go("Slot");function nL(t){const e=T.forwardRef((n,r)=>{const{children:i,...s}=n;if(T.isValidElement(i)){const o=oL(i),l=sL(s,i.props);return i.type!==T.Fragment&&(l.ref=r?Ph(r,o):o),T.cloneElement(i,l)}return T.Children.count(i)>1?T.Children.only(null):null});return e.displayName=`${t}.SlotClone`,e}var l_=Symbol("radix.slottable");function rL(t){const e=({children:n})=>w.jsx(w.Fragment,{children:n});return e.displayName=`${t}.Slottable`,e.__radixId=l_,e}function iL(t){return T.isValidElement(t)&&typeof t.type=="function"&&"__radixId"in t.type&&t.type.__radixId===l_}function sL(t,e){const n={...e};for(const r in e){const i=t[r],s=e[r];/^on[A-Z]/.test(r)?i&&s?n[r]=(...l)=>{const c=s(...l);return i(...l),c}:i&&(n[r]=i):r==="style"?n[r]={...i,...s}:r==="className"&&(n[r]=[i,s].filter(Boolean).join(" "))}return{...t,...n}}function oL(t){let e=Object.getOwnPropertyDescriptor(t.props,"ref")?.get,n=e&&"isReactWarning"in e&&e.isReactWarning;return n?t.ref:(e=Object.getOwnPropertyDescriptor(t,"ref")?.get,n=e&&"isReactWarning"in e&&e.isReactWarning,n?t.props.ref:t.props.ref||t.ref)}var aL=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],xt=aL.reduce((t,e)=>{const n=Go(`Primitive.${e}`),r=T.forwardRef((i,s)=>{const{asChild:o,...l}=i,c=o?n:e;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),w.jsx(c,{...l,ref:s})});return r.displayName=`Primitive.${e}`,{...t,[e]:r}},{});function u_(t,e){t&&Ac.flushSync(()=>t.dispatchEvent(e))}function Yi(t){const e=T.useRef(t);return T.useEffect(()=>{e.current=t}),T.useMemo(()=>(...n)=>e.current?.(...n),[])}function lL(t,e=globalThis?.document){const n=Yi(t);T.useEffect(()=>{const r=i=>{i.key==="Escape"&&n(i)};return e.addEventListener("keydown",r,{capture:!0}),()=>e.removeEventListener("keydown",r,{capture:!0})},[n,e])}var uL="DismissableLayer",k0="dismissableLayer.update",cL="dismissableLayer.pointerDownOutside",dL="dismissableLayer.focusOutside",xv,c_=T.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),kc=T.forwardRef((t,e)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:i,onFocusOutside:s,onInteractOutside:o,onDismiss:l,...c}=t,d=T.useContext(c_),[f,p]=T.useState(null),m=f?.ownerDocument??globalThis?.document,[,g]=T.useState({}),x=Pt(e,D=>p(D)),v=Array.from(d.layers),[S]=[...d.layersWithOutsidePointerEventsDisabled].slice(-1),C=v.indexOf(S),A=f?v.indexOf(f):-1,k=d.layersWithOutsidePointerEventsDisabled.size>0,M=A>=C,F=pL(D=>{const G=D.target,X=[...d.branches].some(P=>P.contains(G));!M||X||(i?.(D),o?.(D),D.defaultPrevented||l?.())},m),I=mL(D=>{const G=D.target;[...d.branches].some(P=>P.contains(G))||(s?.(D),o?.(D),D.defaultPrevented||l?.())},m);return lL(D=>{A===d.layers.size-1&&(r?.(D),!D.defaultPrevented&&l&&(D.preventDefault(),l()))},m),T.useEffect(()=>{if(f)return n&&(d.layersWithOutsidePointerEventsDisabled.size===0&&(xv=m.body.style.pointerEvents,m.body.style.pointerEvents="none"),d.layersWithOutsidePointerEventsDisabled.add(f)),d.layers.add(f),vv(),()=>{n&&d.layersWithOutsidePointerEventsDisabled.size===1&&(m.body.style.pointerEvents=xv)}},[f,m,n,d]),T.useEffect(()=>()=>{f&&(d.layers.delete(f),d.layersWithOutsidePointerEventsDisabled.delete(f),vv())},[f,d]),T.useEffect(()=>{const D=()=>g({});return document.addEventListener(k0,D),()=>document.removeEventListener(k0,D)},[]),w.jsx(xt.div,{...c,ref:x,style:{pointerEvents:k?M?"auto":"none":void 0,...t.style},onFocusCapture:je(t.onFocusCapture,I.onFocusCapture),onBlurCapture:je(t.onBlurCapture,I.onBlurCapture),onPointerDownCapture:je(t.onPointerDownCapture,F.onPointerDownCapture)})});kc.displayName=uL;var fL="DismissableLayerBranch",hL=T.forwardRef((t,e)=>{const n=T.useContext(c_),r=T.useRef(null),i=Pt(e,r);return T.useEffect(()=>{const s=r.current;if(s)return n.branches.add(s),()=>{n.branches.delete(s)}},[n.branches]),w.jsx(xt.div,{...t,ref:i})});hL.displayName=fL;function pL(t,e=globalThis?.document){const n=Yi(t),r=T.useRef(!1),i=T.useRef(()=>{});return T.useEffect(()=>{const s=l=>{if(l.target&&!r.current){let c=function(){d_(cL,n,d,{discrete:!0})};const d={originalEvent:l};l.pointerType==="touch"?(e.removeEventListener("click",i.current),i.current=c,e.addEventListener("click",i.current,{once:!0})):c()}else e.removeEventListener("click",i.current);r.current=!1},o=window.setTimeout(()=>{e.addEventListener("pointerdown",s)},0);return()=>{window.clearTimeout(o),e.removeEventListener("pointerdown",s),e.removeEventListener("click",i.current)}},[e,n]),{onPointerDownCapture:()=>r.current=!0}}function mL(t,e=globalThis?.document){const n=Yi(t),r=T.useRef(!1);return T.useEffect(()=>{const i=s=>{s.target&&!r.current&&d_(dL,n,{originalEvent:s},{discrete:!1})};return e.addEventListener("focusin",i),()=>e.removeEventListener("focusin",i)},[e,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}function vv(){const t=new CustomEvent(k0);document.dispatchEvent(t)}function d_(t,e,n,{discrete:r}){const i=n.originalEvent.target,s=new CustomEvent(t,{bubbles:!1,cancelable:!0,detail:n});e&&i.addEventListener(t,e,{once:!0}),r?u_(i,s):i.dispatchEvent(s)}var lr=globalThis?.document?T.useLayoutEffect:()=>{},gL=Qb[" useId ".trim().toString()]||(()=>{}),bL=0;function Gi(t){const[e,n]=T.useState(gL());return lr(()=>{n(r=>r??String(bL++))},[t]),e?`radix-${e}`:""}const EL=["top","right","bottom","left"],uo=Math.min,$r=Math.max,Qf=Math.round,ef=Math.floor,Ki=t=>({x:t,y:t}),yL={left:"right",right:"left",bottom:"top",top:"bottom"},xL={start:"end",end:"start"};function N0(t,e,n){return $r(t,uo(e,n))}function ys(t,e){return typeof t=="function"?t(e):t}function xs(t){return t.split("-")[0]}function Nl(t){return t.split("-")[1]}function tE(t){return t==="x"?"y":"x"}function nE(t){return t==="y"?"height":"width"}const vL=new Set(["top","bottom"]);function Vi(t){return vL.has(xs(t))?"y":"x"}function rE(t){return tE(Vi(t))}function wL(t,e,n){n===void 0&&(n=!1);const r=Nl(t),i=rE(t),s=nE(i);let o=i==="x"?r===(n?"end":"start")?"right":"left":r==="start"?"bottom":"top";return e.reference[s]>e.floating[s]&&(o=Zf(o)),[o,Zf(o)]}function TL(t){const e=Zf(t);return[R0(t),e,R0(e)]}function R0(t){return t.replace(/start|end/g,e=>xL[e])}const wv=["left","right"],Tv=["right","left"],SL=["top","bottom"],_L=["bottom","top"];function CL(t,e,n){switch(t){case"top":case"bottom":return n?e?Tv:wv:e?wv:Tv;case"left":case"right":return e?SL:_L;default:return[]}}function AL(t,e,n,r){const i=Nl(t);let s=CL(xs(t),n==="start",r);return i&&(s=s.map(o=>o+"-"+i),e&&(s=s.concat(s.map(R0)))),s}function Zf(t){return t.replace(/left|right|bottom|top/g,e=>yL[e])}function kL(t){return{top:0,right:0,bottom:0,left:0,...t}}function f_(t){return typeof t!="number"?kL(t):{top:t,right:t,bottom:t,left:t}}function Jf(t){const{x:e,y:n,width:r,height:i}=t;return{width:r,height:i,top:n,left:e,right:e+r,bottom:n+i,x:e,y:n}}function Sv(t,e,n){let{reference:r,floating:i}=t;const s=Vi(e),o=rE(e),l=nE(o),c=xs(e),d=s==="y",f=r.x+r.width/2-i.width/2,p=r.y+r.height/2-i.height/2,m=r[l]/2-i[l]/2;let g;switch(c){case"top":g={x:f,y:r.y-i.height};break;case"bottom":g={x:f,y:r.y+r.height};break;case"right":g={x:r.x+r.width,y:p};break;case"left":g={x:r.x-i.width,y:p};break;default:g={x:r.x,y:r.y}}switch(Nl(e)){case"start":g[o]-=m*(n&&d?-1:1);break;case"end":g[o]+=m*(n&&d?-1:1);break}return g}const NL=async(t,e,n)=>{const{placement:r="bottom",strategy:i="absolute",middleware:s=[],platform:o}=n,l=s.filter(Boolean),c=await(o.isRTL==null?void 0:o.isRTL(e));let d=await o.getElementRects({reference:t,floating:e,strategy:i}),{x:f,y:p}=Sv(d,r,c),m=r,g={},x=0;for(let v=0;v({name:"arrow",options:t,async fn(e){const{x:n,y:r,placement:i,rects:s,platform:o,elements:l,middlewareData:c}=e,{element:d,padding:f=0}=ys(t,e)||{};if(d==null)return{};const p=f_(f),m={x:n,y:r},g=rE(i),x=nE(g),v=await o.getDimensions(d),S=g==="y",C=S?"top":"left",A=S?"bottom":"right",k=S?"clientHeight":"clientWidth",M=s.reference[x]+s.reference[g]-m[g]-s.floating[x],F=m[g]-s.reference[g],I=await(o.getOffsetParent==null?void 0:o.getOffsetParent(d));let D=I?I[k]:0;(!D||!await(o.isElement==null?void 0:o.isElement(I)))&&(D=l.floating[k]||s.floating[x]);const G=M/2-F/2,X=D/2-v[x]/2-1,P=uo(p[C],X),Y=uo(p[A],X),z=P,ie=D-v[x]-Y,Z=D/2-v[x]/2+G,ee=N0(z,Z,ie),ae=!c.arrow&&Nl(i)!=null&&Z!==ee&&s.reference[x]/2-(ZZ<=0)){var Y,z;const Z=(((Y=s.flip)==null?void 0:Y.index)||0)+1,ee=D[Z];if(ee&&(!(p==="alignment"?A!==Vi(ee):!1)||P.every(j=>j.overflows[0]>0&&Vi(j.placement)===A)))return{data:{index:Z,overflows:P},reset:{placement:ee}};let ae=(z=P.filter(de=>de.overflows[0]<=0).sort((de,j)=>de.overflows[1]-j.overflows[1])[0])==null?void 0:z.placement;if(!ae)switch(g){case"bestFit":{var ie;const de=(ie=P.filter(j=>{if(I){const W=Vi(j.placement);return W===A||W==="y"}return!0}).map(j=>[j.placement,j.overflows.filter(W=>W>0).reduce((W,O)=>W+O,0)]).sort((j,W)=>j[1]-W[1])[0])==null?void 0:ie[0];de&&(ae=de);break}case"initialPlacement":ae=l;break}if(i!==ae)return{reset:{placement:ae}}}return{}}}};function _v(t,e){return{top:t.top-e.height,right:t.right-e.width,bottom:t.bottom-e.height,left:t.left-e.width}}function Cv(t){return EL.some(e=>t[e]>=0)}const OL=function(t){return t===void 0&&(t={}),{name:"hide",options:t,async fn(e){const{rects:n}=e,{strategy:r="referenceHidden",...i}=ys(t,e);switch(r){case"referenceHidden":{const s=await oc(e,{...i,elementContext:"reference"}),o=_v(s,n.reference);return{data:{referenceHiddenOffsets:o,referenceHidden:Cv(o)}}}case"escaped":{const s=await oc(e,{...i,altBoundary:!0}),o=_v(s,n.floating);return{data:{escapedOffsets:o,escaped:Cv(o)}}}default:return{}}}}},h_=new Set(["left","top"]);async function ML(t,e){const{placement:n,platform:r,elements:i}=t,s=await(r.isRTL==null?void 0:r.isRTL(i.floating)),o=xs(n),l=Nl(n),c=Vi(n)==="y",d=h_.has(o)?-1:1,f=s&&c?-1:1,p=ys(e,t);let{mainAxis:m,crossAxis:g,alignmentAxis:x}=typeof p=="number"?{mainAxis:p,crossAxis:0,alignmentAxis:null}:{mainAxis:p.mainAxis||0,crossAxis:p.crossAxis||0,alignmentAxis:p.alignmentAxis};return l&&typeof x=="number"&&(g=l==="end"?x*-1:x),c?{x:g*f,y:m*d}:{x:m*d,y:g*f}}const DL=function(t){return t===void 0&&(t=0),{name:"offset",options:t,async fn(e){var n,r;const{x:i,y:s,placement:o,middlewareData:l}=e,c=await ML(e,t);return o===((n=l.offset)==null?void 0:n.placement)&&(r=l.arrow)!=null&&r.alignmentOffset?{}:{x:i+c.x,y:s+c.y,data:{...c,placement:o}}}}},LL=function(t){return t===void 0&&(t={}),{name:"shift",options:t,async fn(e){const{x:n,y:r,placement:i}=e,{mainAxis:s=!0,crossAxis:o=!1,limiter:l={fn:S=>{let{x:C,y:A}=S;return{x:C,y:A}}},...c}=ys(t,e),d={x:n,y:r},f=await oc(e,c),p=Vi(xs(i)),m=tE(p);let g=d[m],x=d[p];if(s){const S=m==="y"?"top":"left",C=m==="y"?"bottom":"right",A=g+f[S],k=g-f[C];g=N0(A,g,k)}if(o){const S=p==="y"?"top":"left",C=p==="y"?"bottom":"right",A=x+f[S],k=x-f[C];x=N0(A,x,k)}const v=l.fn({...e,[m]:g,[p]:x});return{...v,data:{x:v.x-n,y:v.y-r,enabled:{[m]:s,[p]:o}}}}}},PL=function(t){return t===void 0&&(t={}),{options:t,fn(e){const{x:n,y:r,placement:i,rects:s,middlewareData:o}=e,{offset:l=0,mainAxis:c=!0,crossAxis:d=!0}=ys(t,e),f={x:n,y:r},p=Vi(i),m=tE(p);let g=f[m],x=f[p];const v=ys(l,e),S=typeof v=="number"?{mainAxis:v,crossAxis:0}:{mainAxis:0,crossAxis:0,...v};if(c){const k=m==="y"?"height":"width",M=s.reference[m]-s.floating[k]+S.mainAxis,F=s.reference[m]+s.reference[k]-S.mainAxis;gF&&(g=F)}if(d){var C,A;const k=m==="y"?"width":"height",M=h_.has(xs(i)),F=s.reference[p]-s.floating[k]+(M&&((C=o.offset)==null?void 0:C[p])||0)+(M?0:S.crossAxis),I=s.reference[p]+s.reference[k]+(M?0:((A=o.offset)==null?void 0:A[p])||0)-(M?S.crossAxis:0);xI&&(x=I)}return{[m]:g,[p]:x}}}},FL=function(t){return t===void 0&&(t={}),{name:"size",options:t,async fn(e){var n,r;const{placement:i,rects:s,platform:o,elements:l}=e,{apply:c=()=>{},...d}=ys(t,e),f=await oc(e,d),p=xs(i),m=Nl(i),g=Vi(i)==="y",{width:x,height:v}=s.floating;let S,C;p==="top"||p==="bottom"?(S=p,C=m===(await(o.isRTL==null?void 0:o.isRTL(l.floating))?"start":"end")?"left":"right"):(C=p,S=m==="end"?"top":"bottom");const A=v-f.top-f.bottom,k=x-f.left-f.right,M=uo(v-f[S],A),F=uo(x-f[C],k),I=!e.middlewareData.shift;let D=M,G=F;if((n=e.middlewareData.shift)!=null&&n.enabled.x&&(G=k),(r=e.middlewareData.shift)!=null&&r.enabled.y&&(D=A),I&&!m){const P=$r(f.left,0),Y=$r(f.right,0),z=$r(f.top,0),ie=$r(f.bottom,0);g?G=x-2*(P!==0||Y!==0?P+Y:$r(f.left,f.right)):D=v-2*(z!==0||ie!==0?z+ie:$r(f.top,f.bottom))}await c({...e,availableWidth:G,availableHeight:D});const X=await o.getDimensions(l.floating);return x!==X.width||v!==X.height?{reset:{rects:!0}}:{}}}};function Fh(){return typeof window<"u"}function Rl(t){return p_(t)?(t.nodeName||"").toLowerCase():"#document"}function Vr(t){var e;return(t==null||(e=t.ownerDocument)==null?void 0:e.defaultView)||window}function Qi(t){var e;return(e=(p_(t)?t.ownerDocument:t.document)||window.document)==null?void 0:e.documentElement}function p_(t){return Fh()?t instanceof Node||t instanceof Vr(t).Node:!1}function Ti(t){return Fh()?t instanceof Element||t instanceof Vr(t).Element:!1}function qi(t){return Fh()?t instanceof HTMLElement||t instanceof Vr(t).HTMLElement:!1}function Av(t){return!Fh()||typeof ShadowRoot>"u"?!1:t instanceof ShadowRoot||t instanceof Vr(t).ShadowRoot}const BL=new Set(["inline","contents"]);function Nc(t){const{overflow:e,overflowX:n,overflowY:r,display:i}=Si(t);return/auto|scroll|overlay|hidden|clip/.test(e+r+n)&&!BL.has(i)}const UL=new Set(["table","td","th"]);function HL(t){return UL.has(Rl(t))}const zL=[":popover-open",":modal"];function Bh(t){return zL.some(e=>{try{return t.matches(e)}catch{return!1}})}const jL=["transform","translate","scale","rotate","perspective"],$L=["transform","translate","scale","rotate","perspective","filter"],WL=["paint","layout","strict","content"];function iE(t){const e=sE(),n=Ti(t)?Si(t):t;return jL.some(r=>n[r]?n[r]!=="none":!1)||(n.containerType?n.containerType!=="normal":!1)||!e&&(n.backdropFilter?n.backdropFilter!=="none":!1)||!e&&(n.filter?n.filter!=="none":!1)||$L.some(r=>(n.willChange||"").includes(r))||WL.some(r=>(n.contain||"").includes(r))}function VL(t){let e=co(t);for(;qi(e)&&!pl(e);){if(iE(e))return e;if(Bh(e))return null;e=co(e)}return null}function sE(){return typeof CSS>"u"||!CSS.supports?!1:CSS.supports("-webkit-backdrop-filter","none")}const GL=new Set(["html","body","#document"]);function pl(t){return GL.has(Rl(t))}function Si(t){return Vr(t).getComputedStyle(t)}function Uh(t){return Ti(t)?{scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}:{scrollLeft:t.scrollX,scrollTop:t.scrollY}}function co(t){if(Rl(t)==="html")return t;const e=t.assignedSlot||t.parentNode||Av(t)&&t.host||Qi(t);return Av(e)?e.host:e}function m_(t){const e=co(t);return pl(e)?t.ownerDocument?t.ownerDocument.body:t.body:qi(e)&&Nc(e)?e:m_(e)}function ac(t,e,n){var r;e===void 0&&(e=[]),n===void 0&&(n=!0);const i=m_(t),s=i===((r=t.ownerDocument)==null?void 0:r.body),o=Vr(i);if(s){const l=I0(o);return e.concat(o,o.visualViewport||[],Nc(i)?i:[],l&&n?ac(l):[])}return e.concat(i,ac(i,[],n))}function I0(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function g_(t){const e=Si(t);let n=parseFloat(e.width)||0,r=parseFloat(e.height)||0;const i=qi(t),s=i?t.offsetWidth:n,o=i?t.offsetHeight:r,l=Qf(n)!==s||Qf(r)!==o;return l&&(n=s,r=o),{width:n,height:r,$:l}}function oE(t){return Ti(t)?t:t.contextElement}function rl(t){const e=oE(t);if(!qi(e))return Ki(1);const n=e.getBoundingClientRect(),{width:r,height:i,$:s}=g_(e);let o=(s?Qf(n.width):n.width)/r,l=(s?Qf(n.height):n.height)/i;return(!o||!Number.isFinite(o))&&(o=1),(!l||!Number.isFinite(l))&&(l=1),{x:o,y:l}}const KL=Ki(0);function b_(t){const e=Vr(t);return!sE()||!e.visualViewport?KL:{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}}function YL(t,e,n){return e===void 0&&(e=!1),!n||e&&n!==Vr(t)?!1:e}function Ko(t,e,n,r){e===void 0&&(e=!1),n===void 0&&(n=!1);const i=t.getBoundingClientRect(),s=oE(t);let o=Ki(1);e&&(r?Ti(r)&&(o=rl(r)):o=rl(t));const l=YL(s,n,r)?b_(s):Ki(0);let c=(i.left+l.x)/o.x,d=(i.top+l.y)/o.y,f=i.width/o.x,p=i.height/o.y;if(s){const m=Vr(s),g=r&&Ti(r)?Vr(r):r;let x=m,v=I0(x);for(;v&&r&&g!==x;){const S=rl(v),C=v.getBoundingClientRect(),A=Si(v),k=C.left+(v.clientLeft+parseFloat(A.paddingLeft))*S.x,M=C.top+(v.clientTop+parseFloat(A.paddingTop))*S.y;c*=S.x,d*=S.y,f*=S.x,p*=S.y,c+=k,d+=M,x=Vr(v),v=I0(x)}}return Jf({width:f,height:p,x:c,y:d})}function aE(t,e){const n=Uh(t).scrollLeft;return e?e.left+n:Ko(Qi(t)).left+n}function E_(t,e,n){n===void 0&&(n=!1);const r=t.getBoundingClientRect(),i=r.left+e.scrollLeft-(n?0:aE(t,r)),s=r.top+e.scrollTop;return{x:i,y:s}}function qL(t){let{elements:e,rect:n,offsetParent:r,strategy:i}=t;const s=i==="fixed",o=Qi(r),l=e?Bh(e.floating):!1;if(r===o||l&&s)return n;let c={scrollLeft:0,scrollTop:0},d=Ki(1);const f=Ki(0),p=qi(r);if((p||!p&&!s)&&((Rl(r)!=="body"||Nc(o))&&(c=Uh(r)),qi(r))){const g=Ko(r);d=rl(r),f.x=g.x+r.clientLeft,f.y=g.y+r.clientTop}const m=o&&!p&&!s?E_(o,c,!0):Ki(0);return{width:n.width*d.x,height:n.height*d.y,x:n.x*d.x-c.scrollLeft*d.x+f.x+m.x,y:n.y*d.y-c.scrollTop*d.y+f.y+m.y}}function XL(t){return Array.from(t.getClientRects())}function QL(t){const e=Qi(t),n=Uh(t),r=t.ownerDocument.body,i=$r(e.scrollWidth,e.clientWidth,r.scrollWidth,r.clientWidth),s=$r(e.scrollHeight,e.clientHeight,r.scrollHeight,r.clientHeight);let o=-n.scrollLeft+aE(t);const l=-n.scrollTop;return Si(r).direction==="rtl"&&(o+=$r(e.clientWidth,r.clientWidth)-i),{width:i,height:s,x:o,y:l}}function ZL(t,e){const n=Vr(t),r=Qi(t),i=n.visualViewport;let s=r.clientWidth,o=r.clientHeight,l=0,c=0;if(i){s=i.width,o=i.height;const d=sE();(!d||d&&e==="fixed")&&(l=i.offsetLeft,c=i.offsetTop)}return{width:s,height:o,x:l,y:c}}const JL=new Set(["absolute","fixed"]);function eP(t,e){const n=Ko(t,!0,e==="fixed"),r=n.top+t.clientTop,i=n.left+t.clientLeft,s=qi(t)?rl(t):Ki(1),o=t.clientWidth*s.x,l=t.clientHeight*s.y,c=i*s.x,d=r*s.y;return{width:o,height:l,x:c,y:d}}function kv(t,e,n){let r;if(e==="viewport")r=ZL(t,n);else if(e==="document")r=QL(Qi(t));else if(Ti(e))r=eP(e,n);else{const i=b_(t);r={x:e.x-i.x,y:e.y-i.y,width:e.width,height:e.height}}return Jf(r)}function y_(t,e){const n=co(t);return n===e||!Ti(n)||pl(n)?!1:Si(n).position==="fixed"||y_(n,e)}function tP(t,e){const n=e.get(t);if(n)return n;let r=ac(t,[],!1).filter(l=>Ti(l)&&Rl(l)!=="body"),i=null;const s=Si(t).position==="fixed";let o=s?co(t):t;for(;Ti(o)&&!pl(o);){const l=Si(o),c=iE(o);!c&&l.position==="fixed"&&(i=null),(s?!c&&!i:!c&&l.position==="static"&&!!i&&JL.has(i.position)||Nc(o)&&!c&&y_(t,o))?r=r.filter(f=>f!==o):i=l,o=co(o)}return e.set(t,r),r}function nP(t){let{element:e,boundary:n,rootBoundary:r,strategy:i}=t;const o=[...n==="clippingAncestors"?Bh(e)?[]:tP(e,this._c):[].concat(n),r],l=o[0],c=o.reduce((d,f)=>{const p=kv(e,f,i);return d.top=$r(p.top,d.top),d.right=uo(p.right,d.right),d.bottom=uo(p.bottom,d.bottom),d.left=$r(p.left,d.left),d},kv(e,l,i));return{width:c.right-c.left,height:c.bottom-c.top,x:c.left,y:c.top}}function rP(t){const{width:e,height:n}=g_(t);return{width:e,height:n}}function iP(t,e,n){const r=qi(e),i=Qi(e),s=n==="fixed",o=Ko(t,!0,s,e);let l={scrollLeft:0,scrollTop:0};const c=Ki(0);function d(){c.x=aE(i)}if(r||!r&&!s)if((Rl(e)!=="body"||Nc(i))&&(l=Uh(e)),r){const g=Ko(e,!0,s,e);c.x=g.x+e.clientLeft,c.y=g.y+e.clientTop}else i&&d();s&&!r&&i&&d();const f=i&&!r&&!s?E_(i,l):Ki(0),p=o.left+l.scrollLeft-c.x-f.x,m=o.top+l.scrollTop-c.y-f.y;return{x:p,y:m,width:o.width,height:o.height}}function mg(t){return Si(t).position==="static"}function Nv(t,e){if(!qi(t)||Si(t).position==="fixed")return null;if(e)return e(t);let n=t.offsetParent;return Qi(t)===n&&(n=n.ownerDocument.body),n}function x_(t,e){const n=Vr(t);if(Bh(t))return n;if(!qi(t)){let i=co(t);for(;i&&!pl(i);){if(Ti(i)&&!mg(i))return i;i=co(i)}return n}let r=Nv(t,e);for(;r&&HL(r)&&mg(r);)r=Nv(r,e);return r&&pl(r)&&mg(r)&&!iE(r)?n:r||VL(t)||n}const sP=async function(t){const e=this.getOffsetParent||x_,n=this.getDimensions,r=await n(t.floating);return{reference:iP(t.reference,await e(t.floating),t.strategy),floating:{x:0,y:0,width:r.width,height:r.height}}};function oP(t){return Si(t).direction==="rtl"}const aP={convertOffsetParentRelativeRectToViewportRelativeRect:qL,getDocumentElement:Qi,getClippingRect:nP,getOffsetParent:x_,getElementRects:sP,getClientRects:XL,getDimensions:rP,getScale:rl,isElement:Ti,isRTL:oP};function v_(t,e){return t.x===e.x&&t.y===e.y&&t.width===e.width&&t.height===e.height}function lP(t,e){let n=null,r;const i=Qi(t);function s(){var l;clearTimeout(r),(l=n)==null||l.disconnect(),n=null}function o(l,c){l===void 0&&(l=!1),c===void 0&&(c=1),s();const d=t.getBoundingClientRect(),{left:f,top:p,width:m,height:g}=d;if(l||e(),!m||!g)return;const x=ef(p),v=ef(i.clientWidth-(f+m)),S=ef(i.clientHeight-(p+g)),C=ef(f),k={rootMargin:-x+"px "+-v+"px "+-S+"px "+-C+"px",threshold:$r(0,uo(1,c))||1};let M=!0;function F(I){const D=I[0].intersectionRatio;if(D!==c){if(!M)return o();D?o(!1,D):r=setTimeout(()=>{o(!1,1e-7)},1e3)}D===1&&!v_(d,t.getBoundingClientRect())&&o(),M=!1}try{n=new IntersectionObserver(F,{...k,root:i.ownerDocument})}catch{n=new IntersectionObserver(F,k)}n.observe(t)}return o(!0),s}function uP(t,e,n,r){r===void 0&&(r={});const{ancestorScroll:i=!0,ancestorResize:s=!0,elementResize:o=typeof ResizeObserver=="function",layoutShift:l=typeof IntersectionObserver=="function",animationFrame:c=!1}=r,d=oE(t),f=i||s?[...d?ac(d):[],...ac(e)]:[];f.forEach(C=>{i&&C.addEventListener("scroll",n,{passive:!0}),s&&C.addEventListener("resize",n)});const p=d&&l?lP(d,n):null;let m=-1,g=null;o&&(g=new ResizeObserver(C=>{let[A]=C;A&&A.target===d&&g&&(g.unobserve(e),cancelAnimationFrame(m),m=requestAnimationFrame(()=>{var k;(k=g)==null||k.observe(e)})),n()}),d&&!c&&g.observe(d),g.observe(e));let x,v=c?Ko(t):null;c&&S();function S(){const C=Ko(t);v&&!v_(v,C)&&n(),v=C,x=requestAnimationFrame(S)}return n(),()=>{var C;f.forEach(A=>{i&&A.removeEventListener("scroll",n),s&&A.removeEventListener("resize",n)}),p?.(),(C=g)==null||C.disconnect(),g=null,c&&cancelAnimationFrame(x)}}const cP=DL,dP=LL,fP=IL,hP=FL,pP=OL,Rv=RL,mP=PL,gP=(t,e,n)=>{const r=new Map,i={platform:aP,...n},s={...i.platform,_c:r};return NL(t,e,{...i,platform:s})};var bP=typeof document<"u",EP=function(){},Lf=bP?T.useLayoutEffect:EP;function eh(t,e){if(t===e)return!0;if(typeof t!=typeof e)return!1;if(typeof t=="function"&&t.toString()===e.toString())return!0;let n,r,i;if(t&&e&&typeof t=="object"){if(Array.isArray(t)){if(n=t.length,n!==e.length)return!1;for(r=n;r--!==0;)if(!eh(t[r],e[r]))return!1;return!0}if(i=Object.keys(t),n=i.length,n!==Object.keys(e).length)return!1;for(r=n;r--!==0;)if(!{}.hasOwnProperty.call(e,i[r]))return!1;for(r=n;r--!==0;){const s=i[r];if(!(s==="_owner"&&t.$$typeof)&&!eh(t[s],e[s]))return!1}return!0}return t!==t&&e!==e}function w_(t){return typeof window>"u"?1:(t.ownerDocument.defaultView||window).devicePixelRatio||1}function Iv(t,e){const n=w_(t);return Math.round(e*n)/n}function gg(t){const e=T.useRef(t);return Lf(()=>{e.current=t}),e}function yP(t){t===void 0&&(t={});const{placement:e="bottom",strategy:n="absolute",middleware:r=[],platform:i,elements:{reference:s,floating:o}={},transform:l=!0,whileElementsMounted:c,open:d}=t,[f,p]=T.useState({x:0,y:0,strategy:n,placement:e,middlewareData:{},isPositioned:!1}),[m,g]=T.useState(r);eh(m,r)||g(r);const[x,v]=T.useState(null),[S,C]=T.useState(null),A=T.useCallback(j=>{j!==I.current&&(I.current=j,v(j))},[]),k=T.useCallback(j=>{j!==D.current&&(D.current=j,C(j))},[]),M=s||x,F=o||S,I=T.useRef(null),D=T.useRef(null),G=T.useRef(f),X=c!=null,P=gg(c),Y=gg(i),z=gg(d),ie=T.useCallback(()=>{if(!I.current||!D.current)return;const j={placement:e,strategy:n,middleware:m};Y.current&&(j.platform=Y.current),gP(I.current,D.current,j).then(W=>{const O={...W,isPositioned:z.current!==!1};Z.current&&!eh(G.current,O)&&(G.current=O,Ac.flushSync(()=>{p(O)}))})},[m,e,n,Y,z]);Lf(()=>{d===!1&&G.current.isPositioned&&(G.current.isPositioned=!1,p(j=>({...j,isPositioned:!1})))},[d]);const Z=T.useRef(!1);Lf(()=>(Z.current=!0,()=>{Z.current=!1}),[]),Lf(()=>{if(M&&(I.current=M),F&&(D.current=F),M&&F){if(P.current)return P.current(M,F,ie);ie()}},[M,F,ie,P,X]);const ee=T.useMemo(()=>({reference:I,floating:D,setReference:A,setFloating:k}),[A,k]),ae=T.useMemo(()=>({reference:M,floating:F}),[M,F]),de=T.useMemo(()=>{const j={position:n,left:0,top:0};if(!ae.floating)return j;const W=Iv(ae.floating,f.x),O=Iv(ae.floating,f.y);return l?{...j,transform:"translate("+W+"px, "+O+"px)",...w_(ae.floating)>=1.5&&{willChange:"transform"}}:{position:n,left:W,top:O}},[n,l,ae.floating,f.x,f.y]);return T.useMemo(()=>({...f,update:ie,refs:ee,elements:ae,floatingStyles:de}),[f,ie,ee,ae,de])}const xP=t=>{function e(n){return{}.hasOwnProperty.call(n,"current")}return{name:"arrow",options:t,fn(n){const{element:r,padding:i}=typeof t=="function"?t(n):t;return r&&e(r)?r.current!=null?Rv({element:r.current,padding:i}).fn(n):{}:r?Rv({element:r,padding:i}).fn(n):{}}}},vP=(t,e)=>({...cP(t),options:[t,e]}),wP=(t,e)=>({...dP(t),options:[t,e]}),TP=(t,e)=>({...mP(t),options:[t,e]}),SP=(t,e)=>({...fP(t),options:[t,e]}),_P=(t,e)=>({...hP(t),options:[t,e]}),CP=(t,e)=>({...pP(t),options:[t,e]}),AP=(t,e)=>({...xP(t),options:[t,e]});var kP="Arrow",T_=T.forwardRef((t,e)=>{const{children:n,width:r=10,height:i=5,...s}=t;return w.jsx(xt.svg,{...s,ref:e,width:r,height:i,viewBox:"0 0 30 10",preserveAspectRatio:"none",children:t.asChild?n:w.jsx("polygon",{points:"0,0 30,0 15,10"})})});T_.displayName=kP;var NP=T_;function S_(t){const[e,n]=T.useState(void 0);return lr(()=>{if(t){n({width:t.offsetWidth,height:t.offsetHeight});const r=new ResizeObserver(i=>{if(!Array.isArray(i)||!i.length)return;const s=i[0];let o,l;if("borderBoxSize"in s){const c=s.borderBoxSize,d=Array.isArray(c)?c[0]:c;o=d.inlineSize,l=d.blockSize}else o=t.offsetWidth,l=t.offsetHeight;n({width:o,height:l})});return r.observe(t,{box:"border-box"}),()=>r.unobserve(t)}else n(void 0)},[t]),e}var lE="Popper",[__,Il]=Cs(lE),[RP,C_]=__(lE),A_=t=>{const{__scopePopper:e,children:n}=t,[r,i]=T.useState(null);return w.jsx(RP,{scope:e,anchor:r,onAnchorChange:i,children:n})};A_.displayName=lE;var k_="PopperAnchor",N_=T.forwardRef((t,e)=>{const{__scopePopper:n,virtualRef:r,...i}=t,s=C_(k_,n),o=T.useRef(null),l=Pt(e,o);return T.useEffect(()=>{s.onAnchorChange(r?.current||o.current)}),r?null:w.jsx(xt.div,{...i,ref:l})});N_.displayName=k_;var uE="PopperContent",[IP,OP]=__(uE),R_=T.forwardRef((t,e)=>{const{__scopePopper:n,side:r="bottom",sideOffset:i=0,align:s="center",alignOffset:o=0,arrowPadding:l=0,avoidCollisions:c=!0,collisionBoundary:d=[],collisionPadding:f=0,sticky:p="partial",hideWhenDetached:m=!1,updatePositionStrategy:g="optimized",onPlaced:x,...v}=t,S=C_(uE,n),[C,A]=T.useState(null),k=Pt(e,J=>A(J)),[M,F]=T.useState(null),I=S_(M),D=I?.width??0,G=I?.height??0,X=r+(s!=="center"?"-"+s:""),P=typeof f=="number"?f:{top:0,right:0,bottom:0,left:0,...f},Y=Array.isArray(d)?d:[d],z=Y.length>0,ie={padding:P,boundary:Y.filter(DP),altBoundary:z},{refs:Z,floatingStyles:ee,placement:ae,isPositioned:de,middlewareData:j}=yP({strategy:"fixed",placement:X,whileElementsMounted:(...J)=>uP(...J,{animationFrame:g==="always"}),elements:{reference:S.anchor},middleware:[vP({mainAxis:i+G,alignmentAxis:o}),c&&wP({mainAxis:!0,crossAxis:!1,limiter:p==="partial"?TP():void 0,...ie}),c&&SP({...ie}),_P({...ie,apply:({elements:J,rects:he,availableWidth:_e,availableHeight:ke})=>{const{width:Ve,height:ot}=he.reference,qe=J.floating.style;qe.setProperty("--radix-popper-available-width",`${_e}px`),qe.setProperty("--radix-popper-available-height",`${ke}px`),qe.setProperty("--radix-popper-anchor-width",`${Ve}px`),qe.setProperty("--radix-popper-anchor-height",`${ot}px`)}}),M&&AP({element:M,padding:l}),LP({arrowWidth:D,arrowHeight:G}),m&&CP({strategy:"referenceHidden",...ie})]}),[W,O]=M_(ae),U=Yi(x);lr(()=>{de&&U?.()},[de,U]);const Q=j.arrow?.x,R=j.arrow?.y,oe=j.arrow?.centerOffset!==0,[pe,ue]=T.useState();return lr(()=>{C&&ue(window.getComputedStyle(C).zIndex)},[C]),w.jsx("div",{ref:Z.setFloating,"data-radix-popper-content-wrapper":"",style:{...ee,transform:de?ee.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:pe,"--radix-popper-transform-origin":[j.transformOrigin?.x,j.transformOrigin?.y].join(" "),...j.hide?.referenceHidden&&{visibility:"hidden",pointerEvents:"none"}},dir:t.dir,children:w.jsx(IP,{scope:n,placedSide:W,onArrowChange:F,arrowX:Q,arrowY:R,shouldHideArrow:oe,children:w.jsx(xt.div,{"data-side":W,"data-align":O,...v,ref:k,style:{...v.style,animation:de?void 0:"none"}})})})});R_.displayName=uE;var I_="PopperArrow",MP={top:"bottom",right:"left",bottom:"top",left:"right"},O_=T.forwardRef(function(e,n){const{__scopePopper:r,...i}=e,s=OP(I_,r),o=MP[s.placedSide];return w.jsx("span",{ref:s.onArrowChange,style:{position:"absolute",left:s.arrowX,top:s.arrowY,[o]:0,transformOrigin:{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[s.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[s.placedSide],visibility:s.shouldHideArrow?"hidden":void 0},children:w.jsx(NP,{...i,ref:n,style:{...i.style,display:"block"}})})});O_.displayName=I_;function DP(t){return t!==null}var LP=t=>({name:"transformOrigin",options:t,fn(e){const{placement:n,rects:r,middlewareData:i}=e,o=i.arrow?.centerOffset!==0,l=o?0:t.arrowWidth,c=o?0:t.arrowHeight,[d,f]=M_(n),p={start:"0%",center:"50%",end:"100%"}[f],m=(i.arrow?.x??0)+l/2,g=(i.arrow?.y??0)+c/2;let x="",v="";return d==="bottom"?(x=o?p:`${m}px`,v=`${-c}px`):d==="top"?(x=o?p:`${m}px`,v=`${r.floating.height+c}px`):d==="right"?(x=`${-c}px`,v=o?p:`${g}px`):d==="left"&&(x=`${r.floating.width+c}px`,v=o?p:`${g}px`),{data:{x,y:v}}}});function M_(t){const[e,n="center"]=t.split("-");return[e,n]}var cE=A_,dE=N_,fE=R_,hE=O_,PP="Portal",Hh=T.forwardRef((t,e)=>{const{container:n,...r}=t,[i,s]=T.useState(!1);lr(()=>s(!0),[]);const o=n||i&&globalThis?.document?.body;return o?e_.createPortal(w.jsx(xt.div,{...r,ref:e}),o):null});Hh.displayName=PP;function FP(t,e){return T.useReducer((n,r)=>e[n][r]??n,t)}var Zi=t=>{const{present:e,children:n}=t,r=BP(e),i=typeof n=="function"?n({present:r.isPresent}):T.Children.only(n),s=Pt(r.ref,UP(i));return typeof n=="function"||r.isPresent?T.cloneElement(i,{ref:s}):null};Zi.displayName="Presence";function BP(t){const[e,n]=T.useState(),r=T.useRef(null),i=T.useRef(t),s=T.useRef("none"),o=t?"mounted":"unmounted",[l,c]=FP(o,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return T.useEffect(()=>{const d=tf(r.current);s.current=l==="mounted"?d:"none"},[l]),lr(()=>{const d=r.current,f=i.current;if(f!==t){const m=s.current,g=tf(d);t?c("MOUNT"):g==="none"||d?.display==="none"?c("UNMOUNT"):c(f&&m!==g?"ANIMATION_OUT":"UNMOUNT"),i.current=t}},[t,c]),lr(()=>{if(e){let d;const f=e.ownerDocument.defaultView??window,p=g=>{const v=tf(r.current).includes(g.animationName);if(g.target===e&&v&&(c("ANIMATION_END"),!i.current)){const S=e.style.animationFillMode;e.style.animationFillMode="forwards",d=f.setTimeout(()=>{e.style.animationFillMode==="forwards"&&(e.style.animationFillMode=S)})}},m=g=>{g.target===e&&(s.current=tf(r.current))};return e.addEventListener("animationstart",m),e.addEventListener("animationcancel",p),e.addEventListener("animationend",p),()=>{f.clearTimeout(d),e.removeEventListener("animationstart",m),e.removeEventListener("animationcancel",p),e.removeEventListener("animationend",p)}}else c("ANIMATION_END")},[e,c]),{isPresent:["mounted","unmountSuspended"].includes(l),ref:T.useCallback(d=>{r.current=d?getComputedStyle(d):null,n(d)},[])}}function tf(t){return t?.animationName||"none"}function UP(t){let e=Object.getOwnPropertyDescriptor(t.props,"ref")?.get,n=e&&"isReactWarning"in e&&e.isReactWarning;return n?t.ref:(e=Object.getOwnPropertyDescriptor(t,"ref")?.get,n=e&&"isReactWarning"in e&&e.isReactWarning,n?t.props.ref:t.props.ref||t.ref)}var HP=Qb[" useInsertionEffect ".trim().toString()]||lr;function Yo({prop:t,defaultProp:e,onChange:n=()=>{},caller:r}){const[i,s,o]=zP({defaultProp:e,onChange:n}),l=t!==void 0,c=l?t:i;{const f=T.useRef(t!==void 0);T.useEffect(()=>{const p=f.current;p!==l&&console.warn(`${r} is changing from ${p?"controlled":"uncontrolled"} to ${l?"controlled":"uncontrolled"}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`),f.current=l},[l,r])}const d=T.useCallback(f=>{if(l){const p=jP(f)?f(t):f;p!==t&&o.current?.(p)}else s(f)},[l,t,s,o]);return[c,d]}function zP({defaultProp:t,onChange:e}){const[n,r]=T.useState(t),i=T.useRef(n),s=T.useRef(e);return HP(()=>{s.current=e},[e]),T.useEffect(()=>{i.current!==n&&(s.current?.(n),i.current=n)},[n,i]),[n,r,s]}function jP(t){return typeof t=="function"}var D_=Object.freeze({position:"absolute",border:0,width:1,height:1,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",wordWrap:"normal"}),$P="VisuallyHidden",L_=T.forwardRef((t,e)=>w.jsx(xt.span,{...t,ref:e,style:{...D_,...t.style}}));L_.displayName=$P;var WP=L_,[zh,FX]=Cs("Tooltip",[Il]),jh=Il(),P_="TooltipProvider",VP=700,O0="tooltip.open",[GP,pE]=zh(P_),F_=t=>{const{__scopeTooltip:e,delayDuration:n=VP,skipDelayDuration:r=300,disableHoverableContent:i=!1,children:s}=t,o=T.useRef(!0),l=T.useRef(!1),c=T.useRef(0);return T.useEffect(()=>{const d=c.current;return()=>window.clearTimeout(d)},[]),w.jsx(GP,{scope:e,isOpenDelayedRef:o,delayDuration:n,onOpen:T.useCallback(()=>{window.clearTimeout(c.current),o.current=!1},[]),onClose:T.useCallback(()=>{window.clearTimeout(c.current),c.current=window.setTimeout(()=>o.current=!0,r)},[r]),isPointerInTransitRef:l,onPointerInTransitChange:T.useCallback(d=>{l.current=d},[]),disableHoverableContent:i,children:s})};F_.displayName=P_;var lc="Tooltip",[KP,$h]=zh(lc),B_=t=>{const{__scopeTooltip:e,children:n,open:r,defaultOpen:i,onOpenChange:s,disableHoverableContent:o,delayDuration:l}=t,c=pE(lc,t.__scopeTooltip),d=jh(e),[f,p]=T.useState(null),m=Gi(),g=T.useRef(0),x=o??c.disableHoverableContent,v=l??c.delayDuration,S=T.useRef(!1),[C,A]=Yo({prop:r,defaultProp:i??!1,onChange:D=>{D?(c.onOpen(),document.dispatchEvent(new CustomEvent(O0))):c.onClose(),s?.(D)},caller:lc}),k=T.useMemo(()=>C?S.current?"delayed-open":"instant-open":"closed",[C]),M=T.useCallback(()=>{window.clearTimeout(g.current),g.current=0,S.current=!1,A(!0)},[A]),F=T.useCallback(()=>{window.clearTimeout(g.current),g.current=0,A(!1)},[A]),I=T.useCallback(()=>{window.clearTimeout(g.current),g.current=window.setTimeout(()=>{S.current=!0,A(!0),g.current=0},v)},[v,A]);return T.useEffect(()=>()=>{g.current&&(window.clearTimeout(g.current),g.current=0)},[]),w.jsx(cE,{...d,children:w.jsx(KP,{scope:e,contentId:m,open:C,stateAttribute:k,trigger:f,onTriggerChange:p,onTriggerEnter:T.useCallback(()=>{c.isOpenDelayedRef.current?I():M()},[c.isOpenDelayedRef,I,M]),onTriggerLeave:T.useCallback(()=>{x?F():(window.clearTimeout(g.current),g.current=0)},[F,x]),onOpen:M,onClose:F,disableHoverableContent:x,children:n})})};B_.displayName=lc;var M0="TooltipTrigger",U_=T.forwardRef((t,e)=>{const{__scopeTooltip:n,...r}=t,i=$h(M0,n),s=pE(M0,n),o=jh(n),l=T.useRef(null),c=Pt(e,l,i.onTriggerChange),d=T.useRef(!1),f=T.useRef(!1),p=T.useCallback(()=>d.current=!1,[]);return T.useEffect(()=>()=>document.removeEventListener("pointerup",p),[p]),w.jsx(dE,{asChild:!0,...o,children:w.jsx(xt.button,{"aria-describedby":i.open?i.contentId:void 0,"data-state":i.stateAttribute,...r,ref:c,onPointerMove:je(t.onPointerMove,m=>{m.pointerType!=="touch"&&!f.current&&!s.isPointerInTransitRef.current&&(i.onTriggerEnter(),f.current=!0)}),onPointerLeave:je(t.onPointerLeave,()=>{i.onTriggerLeave(),f.current=!1}),onPointerDown:je(t.onPointerDown,()=>{i.open&&i.onClose(),d.current=!0,document.addEventListener("pointerup",p,{once:!0})}),onFocus:je(t.onFocus,()=>{d.current||i.onOpen()}),onBlur:je(t.onBlur,i.onClose),onClick:je(t.onClick,i.onClose)})})});U_.displayName=M0;var YP="TooltipPortal",[BX,qP]=zh(YP,{forceMount:void 0}),ml="TooltipContent",H_=T.forwardRef((t,e)=>{const n=qP(ml,t.__scopeTooltip),{forceMount:r=n.forceMount,side:i="top",...s}=t,o=$h(ml,t.__scopeTooltip);return w.jsx(Zi,{present:r||o.open,children:o.disableHoverableContent?w.jsx(z_,{side:i,...s,ref:e}):w.jsx(XP,{side:i,...s,ref:e})})}),XP=T.forwardRef((t,e)=>{const n=$h(ml,t.__scopeTooltip),r=pE(ml,t.__scopeTooltip),i=T.useRef(null),s=Pt(e,i),[o,l]=T.useState(null),{trigger:c,onClose:d}=n,f=i.current,{onPointerInTransitChange:p}=r,m=T.useCallback(()=>{l(null),p(!1)},[p]),g=T.useCallback((x,v)=>{const S=x.currentTarget,C={x:x.clientX,y:x.clientY},A=t3(C,S.getBoundingClientRect()),k=n3(C,A),M=r3(v.getBoundingClientRect()),F=s3([...k,...M]);l(F),p(!0)},[p]);return T.useEffect(()=>()=>m(),[m]),T.useEffect(()=>{if(c&&f){const x=S=>g(S,f),v=S=>g(S,c);return c.addEventListener("pointerleave",x),f.addEventListener("pointerleave",v),()=>{c.removeEventListener("pointerleave",x),f.removeEventListener("pointerleave",v)}}},[c,f,g,m]),T.useEffect(()=>{if(o){const x=v=>{const S=v.target,C={x:v.clientX,y:v.clientY},A=c?.contains(S)||f?.contains(S),k=!i3(C,o);A?m():k&&(m(),d())};return document.addEventListener("pointermove",x),()=>document.removeEventListener("pointermove",x)}},[c,f,o,d,m]),w.jsx(z_,{...t,ref:s})}),[QP,ZP]=zh(lc,{isInside:!1}),JP=rL("TooltipContent"),z_=T.forwardRef((t,e)=>{const{__scopeTooltip:n,children:r,"aria-label":i,onEscapeKeyDown:s,onPointerDownOutside:o,...l}=t,c=$h(ml,n),d=jh(n),{onClose:f}=c;return T.useEffect(()=>(document.addEventListener(O0,f),()=>document.removeEventListener(O0,f)),[f]),T.useEffect(()=>{if(c.trigger){const p=m=>{m.target?.contains(c.trigger)&&f()};return window.addEventListener("scroll",p,{capture:!0}),()=>window.removeEventListener("scroll",p,{capture:!0})}},[c.trigger,f]),w.jsx(kc,{asChild:!0,disableOutsidePointerEvents:!1,onEscapeKeyDown:s,onPointerDownOutside:o,onFocusOutside:p=>p.preventDefault(),onDismiss:f,children:w.jsxs(fE,{"data-state":c.stateAttribute,...d,...l,ref:e,style:{...l.style,"--radix-tooltip-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-tooltip-content-available-width":"var(--radix-popper-available-width)","--radix-tooltip-content-available-height":"var(--radix-popper-available-height)","--radix-tooltip-trigger-width":"var(--radix-popper-anchor-width)","--radix-tooltip-trigger-height":"var(--radix-popper-anchor-height)"},children:[w.jsx(JP,{children:r}),w.jsx(QP,{scope:n,isInside:!0,children:w.jsx(WP,{id:c.contentId,role:"tooltip",children:i||r})})]})})});H_.displayName=ml;var j_="TooltipArrow",e3=T.forwardRef((t,e)=>{const{__scopeTooltip:n,...r}=t,i=jh(n);return ZP(j_,n).isInside?null:w.jsx(hE,{...i,...r,ref:e})});e3.displayName=j_;function t3(t,e){const n=Math.abs(e.top-t.y),r=Math.abs(e.bottom-t.y),i=Math.abs(e.right-t.x),s=Math.abs(e.left-t.x);switch(Math.min(n,r,i,s)){case s:return"left";case i:return"right";case n:return"top";case r:return"bottom";default:throw new Error("unreachable")}}function n3(t,e,n=5){const r=[];switch(e){case"top":r.push({x:t.x-n,y:t.y+n},{x:t.x+n,y:t.y+n});break;case"bottom":r.push({x:t.x-n,y:t.y-n},{x:t.x+n,y:t.y-n});break;case"left":r.push({x:t.x+n,y:t.y-n},{x:t.x+n,y:t.y+n});break;case"right":r.push({x:t.x-n,y:t.y-n},{x:t.x-n,y:t.y+n});break}return r}function r3(t){const{top:e,right:n,bottom:r,left:i}=t;return[{x:i,y:e},{x:n,y:e},{x:n,y:r},{x:i,y:r}]}function i3(t,e){const{x:n,y:r}=t;let i=!1;for(let s=0,o=e.length-1;sr!=m>r&&n<(p-d)*(r-f)/(m-f)+d&&(i=!i)}return i}function s3(t){const e=t.slice();return e.sort((n,r)=>n.xr.x?1:n.yr.y?1:0),o3(e)}function o3(t){if(t.length<=1)return t.slice();const e=[];for(let r=0;r=2;){const s=e[e.length-1],o=e[e.length-2];if((s.x-o.x)*(i.y-o.y)>=(s.y-o.y)*(i.x-o.x))e.pop();else break}e.push(i)}e.pop();const n=[];for(let r=t.length-1;r>=0;r--){const i=t[r];for(;n.length>=2;){const s=n[n.length-1],o=n[n.length-2];if((s.x-o.x)*(i.y-o.y)>=(s.y-o.y)*(i.x-o.x))n.pop();else break}n.push(i)}return n.pop(),e.length===1&&n.length===1&&e[0].x===n[0].x&&e[0].y===n[0].y?e:e.concat(n)}var a3=F_,l3=B_,u3=U_,$_=H_;const c3=a3,d3=l3,f3=u3,W_=T.forwardRef(({className:t,sideOffset:e=4,...n},r)=>w.jsx($_,{ref:r,sideOffset:e,className:Rt("z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",t),...n}));W_.displayName=$_.displayName;function Xn({children:t,value:e,className:n,style:r={},side:i="bottom",align:s="center",delayDuration:o=0}){return w.jsx(c3,{children:w.jsxs(d3,{delayDuration:o,children:[w.jsx(f3,{asChild:!0,children:t}),w.jsx(W_,{side:i,align:s,style:{boxShadow:"none",...r},className:Xe("left-[34px] h-[32px] text-[13px] font-normal font-inter rounded-[20px] border border-[#EBECF0] border-solid bg-white p-[5px_16px_7px_16px]",n),children:w.jsx("div",{children:e})})]})})}const Ov=t=>typeof t=="boolean"?`${t}`:t===0?"0":t,Mv=Al,V_=(t,e)=>n=>{var r;if(e?.variants==null)return Mv(t,n?.class,n?.className);const{variants:i,defaultVariants:s}=e,o=Object.keys(i).map(d=>{const f=n?.[d],p=s?.[d];if(f===null)return null;const m=Ov(f)||Ov(p);return i[d][m]}),l=n&&Object.entries(n).reduce((d,f)=>{let[p,m]=f;return m===void 0||(d[p]=m),d},{}),c=e==null||(r=e.compoundVariants)===null||r===void 0?void 0:r.reduce((d,f)=>{let{class:p,className:m,...g}=f;return Object.entries(g).every(x=>{let[v,S]=x;return Array.isArray(S)?S.includes({...s,...l}[v]):{...s,...l}[v]===S})?[...d,p,m]:d},[]);return Mv(t,o,c,n?.class,n?.className)},h3=V_("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground data-[selected=true]:bg-accent",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3",lg:"h-11 rounded-md px-8",icon:"h-10 w-10"}},defaultVariants:{variant:"default",size:"default"}}),An=T.forwardRef(({className:t,variant:e,size:n,asChild:r=!1,...i},s)=>{const o=r?tL:"button";return w.jsx(o,{className:Rt(h3({variant:e,size:n,className:t})),ref:s,...i})});An.displayName="Button";function mE(t){const e=t+"CollectionProvider",[n,r]=Cs(e),[i,s]=n(e,{collectionRef:{current:null},itemMap:new Map}),o=v=>{const{scope:S,children:C}=v,A=we.useRef(null),k=we.useRef(new Map).current;return w.jsx(i,{scope:S,itemMap:k,collectionRef:A,children:C})};o.displayName=e;const l=t+"CollectionSlot",c=Go(l),d=we.forwardRef((v,S)=>{const{scope:C,children:A}=v,k=s(l,C),M=Pt(S,k.collectionRef);return w.jsx(c,{ref:M,children:A})});d.displayName=l;const f=t+"CollectionItemSlot",p="data-radix-collection-item",m=Go(f),g=we.forwardRef((v,S)=>{const{scope:C,children:A,...k}=v,M=we.useRef(null),F=Pt(S,M),I=s(f,C);return we.useEffect(()=>(I.itemMap.set(M,{ref:M,...k}),()=>void I.itemMap.delete(M))),w.jsx(m,{[p]:"",ref:F,children:A})});g.displayName=f;function x(v){const S=s(t+"CollectionConsumer",v);return we.useCallback(()=>{const A=S.collectionRef.current;if(!A)return[];const k=Array.from(A.querySelectorAll(`[${p}]`));return Array.from(S.itemMap.values()).sort((I,D)=>k.indexOf(I.ref.current)-k.indexOf(D.ref.current))},[S.collectionRef,S.itemMap])}return[{Provider:o,Slot:d,ItemSlot:g},x,r]}var p3=T.createContext(void 0);function gE(t){const e=T.useContext(p3);return t||e||"ltr"}var bg=0;function bE(){T.useEffect(()=>{const t=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",t[0]??Dv()),document.body.insertAdjacentElement("beforeend",t[1]??Dv()),bg++,()=>{bg===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(e=>e.remove()),bg--}},[])}function Dv(){const t=document.createElement("span");return t.setAttribute("data-radix-focus-guard",""),t.tabIndex=0,t.style.outline="none",t.style.opacity="0",t.style.position="fixed",t.style.pointerEvents="none",t}var Eg="focusScope.autoFocusOnMount",yg="focusScope.autoFocusOnUnmount",Lv={bubbles:!1,cancelable:!0},m3="FocusScope",Wh=T.forwardRef((t,e)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:i,onUnmountAutoFocus:s,...o}=t,[l,c]=T.useState(null),d=Yi(i),f=Yi(s),p=T.useRef(null),m=Pt(e,v=>c(v)),g=T.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;T.useEffect(()=>{if(r){let v=function(k){if(g.paused||!l)return;const M=k.target;l.contains(M)?p.current=M:Zs(p.current,{select:!0})},S=function(k){if(g.paused||!l)return;const M=k.relatedTarget;M!==null&&(l.contains(M)||Zs(p.current,{select:!0}))},C=function(k){if(document.activeElement===document.body)for(const F of k)F.removedNodes.length>0&&Zs(l)};document.addEventListener("focusin",v),document.addEventListener("focusout",S);const A=new MutationObserver(C);return l&&A.observe(l,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",v),document.removeEventListener("focusout",S),A.disconnect()}}},[r,l,g.paused]),T.useEffect(()=>{if(l){Fv.add(g);const v=document.activeElement;if(!l.contains(v)){const C=new CustomEvent(Eg,Lv);l.addEventListener(Eg,d),l.dispatchEvent(C),C.defaultPrevented||(g3(v3(G_(l)),{select:!0}),document.activeElement===v&&Zs(l))}return()=>{l.removeEventListener(Eg,d),setTimeout(()=>{const C=new CustomEvent(yg,Lv);l.addEventListener(yg,f),l.dispatchEvent(C),C.defaultPrevented||Zs(v??document.body,{select:!0}),l.removeEventListener(yg,f),Fv.remove(g)},0)}}},[l,d,f,g]);const x=T.useCallback(v=>{if(!n&&!r||g.paused)return;const S=v.key==="Tab"&&!v.altKey&&!v.ctrlKey&&!v.metaKey,C=document.activeElement;if(S&&C){const A=v.currentTarget,[k,M]=b3(A);k&&M?!v.shiftKey&&C===M?(v.preventDefault(),n&&Zs(k,{select:!0})):v.shiftKey&&C===k&&(v.preventDefault(),n&&Zs(M,{select:!0})):C===A&&v.preventDefault()}},[n,r,g.paused]);return w.jsx(xt.div,{tabIndex:-1,...o,ref:m,onKeyDown:x})});Wh.displayName=m3;function g3(t,{select:e=!1}={}){const n=document.activeElement;for(const r of t)if(Zs(r,{select:e}),document.activeElement!==n)return}function b3(t){const e=G_(t),n=Pv(e,t),r=Pv(e.reverse(),t);return[n,r]}function G_(t){const e=[],n=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{const i=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||i?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)e.push(n.currentNode);return e}function Pv(t,e){for(const n of t)if(!E3(n,{upTo:e}))return n}function E3(t,{upTo:e}){if(getComputedStyle(t).visibility==="hidden")return!0;for(;t;){if(e!==void 0&&t===e)return!1;if(getComputedStyle(t).display==="none")return!0;t=t.parentElement}return!1}function y3(t){return t instanceof HTMLInputElement&&"select"in t}function Zs(t,{select:e=!1}={}){if(t&&t.focus){const n=document.activeElement;t.focus({preventScroll:!0}),t!==n&&y3(t)&&e&&t.select()}}var Fv=x3();function x3(){let t=[];return{add(e){const n=t[0];e!==n&&n?.pause(),t=Bv(t,e),t.unshift(e)},remove(e){t=Bv(t,e),t[0]?.resume()}}}function Bv(t,e){const n=[...t],r=n.indexOf(e);return r!==-1&&n.splice(r,1),n}function v3(t){return t.filter(e=>e.tagName!=="A")}var xg="rovingFocusGroup.onEntryFocus",w3={bubbles:!1,cancelable:!0},Rc="RovingFocusGroup",[D0,K_,T3]=mE(Rc),[S3,Y_]=Cs(Rc,[T3]),[_3,C3]=S3(Rc),q_=T.forwardRef((t,e)=>w.jsx(D0.Provider,{scope:t.__scopeRovingFocusGroup,children:w.jsx(D0.Slot,{scope:t.__scopeRovingFocusGroup,children:w.jsx(A3,{...t,ref:e})})}));q_.displayName=Rc;var A3=T.forwardRef((t,e)=>{const{__scopeRovingFocusGroup:n,orientation:r,loop:i=!1,dir:s,currentTabStopId:o,defaultCurrentTabStopId:l,onCurrentTabStopIdChange:c,onEntryFocus:d,preventScrollOnEntryFocus:f=!1,...p}=t,m=T.useRef(null),g=Pt(e,m),x=gE(s),[v,S]=Yo({prop:o,defaultProp:l??null,onChange:c,caller:Rc}),[C,A]=T.useState(!1),k=Yi(d),M=K_(n),F=T.useRef(!1),[I,D]=T.useState(0);return T.useEffect(()=>{const G=m.current;if(G)return G.addEventListener(xg,k),()=>G.removeEventListener(xg,k)},[k]),w.jsx(_3,{scope:n,orientation:r,dir:x,loop:i,currentTabStopId:v,onItemFocus:T.useCallback(G=>S(G),[S]),onItemShiftTab:T.useCallback(()=>A(!0),[]),onFocusableItemAdd:T.useCallback(()=>D(G=>G+1),[]),onFocusableItemRemove:T.useCallback(()=>D(G=>G-1),[]),children:w.jsx(xt.div,{tabIndex:C||I===0?-1:0,"data-orientation":r,...p,ref:g,style:{outline:"none",...t.style},onMouseDown:je(t.onMouseDown,()=>{F.current=!0}),onFocus:je(t.onFocus,G=>{const X=!F.current;if(G.target===G.currentTarget&&X&&!C){const P=new CustomEvent(xg,w3);if(G.currentTarget.dispatchEvent(P),!P.defaultPrevented){const Y=M().filter(ae=>ae.focusable),z=Y.find(ae=>ae.active),ie=Y.find(ae=>ae.id===v),ee=[z,ie,...Y].filter(Boolean).map(ae=>ae.ref.current);Z_(ee,f)}}F.current=!1}),onBlur:je(t.onBlur,()=>A(!1))})})}),X_="RovingFocusGroupItem",Q_=T.forwardRef((t,e)=>{const{__scopeRovingFocusGroup:n,focusable:r=!0,active:i=!1,tabStopId:s,children:o,...l}=t,c=Gi(),d=s||c,f=C3(X_,n),p=f.currentTabStopId===d,m=K_(n),{onFocusableItemAdd:g,onFocusableItemRemove:x,currentTabStopId:v}=f;return T.useEffect(()=>{if(r)return g(),()=>x()},[r,g,x]),w.jsx(D0.ItemSlot,{scope:n,id:d,focusable:r,active:i,children:w.jsx(xt.span,{tabIndex:p?0:-1,"data-orientation":f.orientation,...l,ref:e,onMouseDown:je(t.onMouseDown,S=>{r?f.onItemFocus(d):S.preventDefault()}),onFocus:je(t.onFocus,()=>f.onItemFocus(d)),onKeyDown:je(t.onKeyDown,S=>{if(S.key==="Tab"&&S.shiftKey){f.onItemShiftTab();return}if(S.target!==S.currentTarget)return;const C=R3(S,f.orientation,f.dir);if(C!==void 0){if(S.metaKey||S.ctrlKey||S.altKey||S.shiftKey)return;S.preventDefault();let k=m().filter(M=>M.focusable).map(M=>M.ref.current);if(C==="last")k.reverse();else if(C==="prev"||C==="next"){C==="prev"&&k.reverse();const M=k.indexOf(S.currentTarget);k=f.loop?I3(k,M+1):k.slice(M+1)}setTimeout(()=>Z_(k))}}),children:typeof o=="function"?o({isCurrentTabStop:p,hasTabStop:v!=null}):o})})});Q_.displayName=X_;var k3={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function N3(t,e){return e!=="rtl"?t:t==="ArrowLeft"?"ArrowRight":t==="ArrowRight"?"ArrowLeft":t}function R3(t,e,n){const r=N3(t.key,n);if(!(e==="vertical"&&["ArrowLeft","ArrowRight"].includes(r))&&!(e==="horizontal"&&["ArrowUp","ArrowDown"].includes(r)))return k3[r]}function Z_(t,e=!1){const n=document.activeElement;for(const r of t)if(r===n||(r.focus({preventScroll:e}),document.activeElement!==n))return}function I3(t,e){return t.map((n,r)=>t[(e+r)%t.length])}var O3=q_,M3=Q_,D3=function(t){if(typeof document>"u")return null;var e=Array.isArray(t)?t[0]:t;return e.ownerDocument.body},Fa=new WeakMap,nf=new WeakMap,rf={},vg=0,J_=function(t){return t&&(t.host||J_(t.parentNode))},L3=function(t,e){return e.map(function(n){if(t.contains(n))return n;var r=J_(n);return r&&t.contains(r)?r:(console.error("aria-hidden",n,"in not contained inside",t,". Doing nothing"),null)}).filter(function(n){return!!n})},P3=function(t,e,n,r){var i=L3(e,Array.isArray(t)?t:[t]);rf[n]||(rf[n]=new WeakMap);var s=rf[n],o=[],l=new Set,c=new Set(i),d=function(p){!p||l.has(p)||(l.add(p),d(p.parentNode))};i.forEach(d);var f=function(p){!p||c.has(p)||Array.prototype.forEach.call(p.children,function(m){if(l.has(m))f(m);else try{var g=m.getAttribute(r),x=g!==null&&g!=="false",v=(Fa.get(m)||0)+1,S=(s.get(m)||0)+1;Fa.set(m,v),s.set(m,S),o.push(m),v===1&&x&&nf.set(m,!0),S===1&&m.setAttribute(n,"true"),x||m.setAttribute(r,"true")}catch(C){console.error("aria-hidden: cannot operate on ",m,C)}})};return f(e),l.clear(),vg++,function(){o.forEach(function(p){var m=Fa.get(p)-1,g=s.get(p)-1;Fa.set(p,m),s.set(p,g),m||(nf.has(p)||p.removeAttribute(r),nf.delete(p)),g||p.removeAttribute(n)}),vg--,vg||(Fa=new WeakMap,Fa=new WeakMap,nf=new WeakMap,rf={})}},EE=function(t,e,n){n===void 0&&(n="data-aria-hidden");var r=Array.from(Array.isArray(t)?t:[t]),i=D3(t);return i?(r.push.apply(r,Array.from(i.querySelectorAll("[aria-live], script"))),P3(r,i,n,"aria-hidden")):function(){return null}},ji=function(){return ji=Object.assign||function(e){for(var n,r=1,i=arguments.length;r"u")return J3;var e=e6(t),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:e[0],top:e[1],right:e[2],gap:Math.max(0,r-n+e[2]-e[0])}},n6=rC(),il="data-scroll-locked",r6=function(t,e,n,r){var i=t.left,s=t.top,o=t.right,l=t.gap;return n===void 0&&(n="margin"),` .`.concat(B3,` { overflow: hidden `).concat(r,`; padding-right: `).concat(l,"px ").concat(r,`; } body[`).concat(il,`] { overflow: hidden `).concat(r,`; overscroll-behavior: contain; `).concat([e&&"position: relative ".concat(r,";"),n==="margin"&&` padding-left: `.concat(i,`px; padding-top: `).concat(s,`px; padding-right: `).concat(o,`px; margin-left:0; margin-top:0; margin-right: `).concat(l,"px ").concat(r,`; `),n==="padding"&&"padding-right: ".concat(l,"px ").concat(r,";")].filter(Boolean).join(""),` } .`).concat(Pf,` { right: `).concat(l,"px ").concat(r,`; } .`).concat(Ff,` { margin-right: `).concat(l,"px ").concat(r,`; } .`).concat(Pf," .").concat(Pf,` { right: 0 `).concat(r,`; } .`).concat(Ff," .").concat(Ff,` { margin-right: 0 `).concat(r,`; } body[`).concat(il,`] { `).concat(U3,": ").concat(l,`px; } `)},Hv=function(){var t=parseInt(document.body.getAttribute(il)||"0",10);return isFinite(t)?t:0},i6=function(){T.useEffect(function(){return document.body.setAttribute(il,(Hv()+1).toString()),function(){var t=Hv()-1;t<=0?document.body.removeAttribute(il):document.body.setAttribute(il,t.toString())}},[])},s6=function(t){var e=t.noRelative,n=t.noImportant,r=t.gapMode,i=r===void 0?"margin":r;i6();var s=T.useMemo(function(){return t6(i)},[i]);return T.createElement(n6,{styles:r6(s,!e,i,n?"":"!important")})},L0=!1;if(typeof window<"u")try{var sf=Object.defineProperty({},"passive",{get:function(){return L0=!0,!0}});window.addEventListener("test",sf,sf),window.removeEventListener("test",sf,sf)}catch{L0=!1}var Ba=L0?{passive:!1}:!1,o6=function(t){return t.tagName==="TEXTAREA"},iC=function(t,e){if(!(t instanceof Element))return!1;var n=window.getComputedStyle(t);return n[e]!=="hidden"&&!(n.overflowY===n.overflowX&&!o6(t)&&n[e]==="visible")},a6=function(t){return iC(t,"overflowY")},l6=function(t){return iC(t,"overflowX")},zv=function(t,e){var n=e.ownerDocument,r=e;do{typeof ShadowRoot<"u"&&r instanceof ShadowRoot&&(r=r.host);var i=sC(t,r);if(i){var s=oC(t,r),o=s[1],l=s[2];if(o>l)return!0}r=r.parentNode}while(r&&r!==n.body);return!1},u6=function(t){var e=t.scrollTop,n=t.scrollHeight,r=t.clientHeight;return[e,n,r]},c6=function(t){var e=t.scrollLeft,n=t.scrollWidth,r=t.clientWidth;return[e,n,r]},sC=function(t,e){return t==="v"?a6(e):l6(e)},oC=function(t,e){return t==="v"?u6(e):c6(e)},d6=function(t,e){return t==="h"&&e==="rtl"?-1:1},f6=function(t,e,n,r,i){var s=d6(t,window.getComputedStyle(e).direction),o=s*r,l=n.target,c=e.contains(l),d=!1,f=o>0,p=0,m=0;do{if(!l)break;var g=oC(t,l),x=g[0],v=g[1],S=g[2],C=v-S-s*x;(x||C)&&sC(t,l)&&(p+=C,m+=x);var A=l.parentNode;l=A&&A.nodeType===Node.DOCUMENT_FRAGMENT_NODE?A.host:A}while(!c&&l!==document.body||c&&(e.contains(l)||e===l));return(f&&Math.abs(p)<1||!f&&Math.abs(m)<1)&&(d=!0),d},of=function(t){return"changedTouches"in t?[t.changedTouches[0].clientX,t.changedTouches[0].clientY]:[0,0]},jv=function(t){return[t.deltaX,t.deltaY]},$v=function(t){return t&&"current"in t?t.current:t},h6=function(t,e){return t[0]===e[0]&&t[1]===e[1]},p6=function(t){return` .block-interactivity-`.concat(t,` {pointer-events: none;} .allow-interactivity-`).concat(t,` {pointer-events: all;} `)},m6=0,Ua=[];function g6(t){var e=T.useRef([]),n=T.useRef([0,0]),r=T.useRef(),i=T.useState(m6++)[0],s=T.useState(rC)[0],o=T.useRef(t);T.useEffect(function(){o.current=t},[t]),T.useEffect(function(){if(t.inert){document.body.classList.add("block-interactivity-".concat(i));var v=F3([t.lockRef.current],(t.shards||[]).map($v),!0).filter(Boolean);return v.forEach(function(S){return S.classList.add("allow-interactivity-".concat(i))}),function(){document.body.classList.remove("block-interactivity-".concat(i)),v.forEach(function(S){return S.classList.remove("allow-interactivity-".concat(i))})}}},[t.inert,t.lockRef.current,t.shards]);var l=T.useCallback(function(v,S){if("touches"in v&&v.touches.length===2||v.type==="wheel"&&v.ctrlKey)return!o.current.allowPinchZoom;var C=of(v),A=n.current,k="deltaX"in v?v.deltaX:A[0]-C[0],M="deltaY"in v?v.deltaY:A[1]-C[1],F,I=v.target,D=Math.abs(k)>Math.abs(M)?"h":"v";if("touches"in v&&D==="h"&&I.type==="range")return!1;var G=zv(D,I);if(!G)return!0;if(G?F=D:(F=D==="v"?"h":"v",G=zv(D,I)),!G)return!1;if(!r.current&&"changedTouches"in v&&(k||M)&&(r.current=F),!F)return!0;var X=r.current||F;return f6(X,S,v,X==="h"?k:M)},[]),c=T.useCallback(function(v){var S=v;if(!(!Ua.length||Ua[Ua.length-1]!==s)){var C="deltaY"in S?jv(S):of(S),A=e.current.filter(function(F){return F.name===S.type&&(F.target===S.target||S.target===F.shadowParent)&&h6(F.delta,C)})[0];if(A&&A.should){S.cancelable&&S.preventDefault();return}if(!A){var k=(o.current.shards||[]).map($v).filter(Boolean).filter(function(F){return F.contains(S.target)}),M=k.length>0?l(S,k[0]):!o.current.noIsolation;M&&S.cancelable&&S.preventDefault()}}},[]),d=T.useCallback(function(v,S,C,A){var k={name:v,delta:S,target:C,should:A,shadowParent:b6(C)};e.current.push(k),setTimeout(function(){e.current=e.current.filter(function(M){return M!==k})},1)},[]),f=T.useCallback(function(v){n.current=of(v),r.current=void 0},[]),p=T.useCallback(function(v){d(v.type,jv(v),v.target,l(v,t.lockRef.current))},[]),m=T.useCallback(function(v){d(v.type,of(v),v.target,l(v,t.lockRef.current))},[]);T.useEffect(function(){return Ua.push(s),t.setCallbacks({onScrollCapture:p,onWheelCapture:p,onTouchMoveCapture:m}),document.addEventListener("wheel",c,Ba),document.addEventListener("touchmove",c,Ba),document.addEventListener("touchstart",f,Ba),function(){Ua=Ua.filter(function(v){return v!==s}),document.removeEventListener("wheel",c,Ba),document.removeEventListener("touchmove",c,Ba),document.removeEventListener("touchstart",f,Ba)}},[]);var g=t.removeScrollBar,x=t.inert;return T.createElement(T.Fragment,null,x?T.createElement(s,{styles:p6(i)}):null,g?T.createElement(s6,{noRelative:t.noRelative,gapMode:t.gapMode}):null)}function b6(t){for(var e=null;t!==null;)t instanceof ShadowRoot&&(e=t.host,t=t.host),t=t.parentNode;return e}const E6=G3(nC,g6);var Gh=T.forwardRef(function(t,e){return T.createElement(Vh,ji({},t,{ref:e,sideCar:E6}))});Gh.classNames=Vh.classNames;var P0=["Enter"," "],y6=["ArrowDown","PageUp","Home"],aC=["ArrowUp","PageDown","End"],x6=[...y6,...aC],v6={ltr:[...P0,"ArrowRight"],rtl:[...P0,"ArrowLeft"]},w6={ltr:["ArrowLeft"],rtl:["ArrowRight"]},Ic="Menu",[uc,T6,S6]=mE(Ic),[ia,lC]=Cs(Ic,[S6,Il,Y_]),Kh=Il(),uC=Y_(),[_6,sa]=ia(Ic),[C6,Oc]=ia(Ic),cC=t=>{const{__scopeMenu:e,open:n=!1,children:r,dir:i,onOpenChange:s,modal:o=!0}=t,l=Kh(e),[c,d]=T.useState(null),f=T.useRef(!1),p=Yi(s),m=gE(i);return T.useEffect(()=>{const g=()=>{f.current=!0,document.addEventListener("pointerdown",x,{capture:!0,once:!0}),document.addEventListener("pointermove",x,{capture:!0,once:!0})},x=()=>f.current=!1;return document.addEventListener("keydown",g,{capture:!0}),()=>{document.removeEventListener("keydown",g,{capture:!0}),document.removeEventListener("pointerdown",x,{capture:!0}),document.removeEventListener("pointermove",x,{capture:!0})}},[]),w.jsx(cE,{...l,children:w.jsx(_6,{scope:e,open:n,onOpenChange:p,content:c,onContentChange:d,children:w.jsx(C6,{scope:e,onClose:T.useCallback(()=>p(!1),[p]),isUsingKeyboardRef:f,dir:m,modal:o,children:r})})})};cC.displayName=Ic;var A6="MenuAnchor",yE=T.forwardRef((t,e)=>{const{__scopeMenu:n,...r}=t,i=Kh(n);return w.jsx(dE,{...i,...r,ref:e})});yE.displayName=A6;var xE="MenuPortal",[k6,dC]=ia(xE,{forceMount:void 0}),fC=t=>{const{__scopeMenu:e,forceMount:n,children:r,container:i}=t,s=sa(xE,e);return w.jsx(k6,{scope:e,forceMount:n,children:w.jsx(Zi,{present:n||s.open,children:w.jsx(Hh,{asChild:!0,container:i,children:r})})})};fC.displayName=xE;var ri="MenuContent",[N6,vE]=ia(ri),hC=T.forwardRef((t,e)=>{const n=dC(ri,t.__scopeMenu),{forceMount:r=n.forceMount,...i}=t,s=sa(ri,t.__scopeMenu),o=Oc(ri,t.__scopeMenu);return w.jsx(uc.Provider,{scope:t.__scopeMenu,children:w.jsx(Zi,{present:r||s.open,children:w.jsx(uc.Slot,{scope:t.__scopeMenu,children:o.modal?w.jsx(R6,{...i,ref:e}):w.jsx(I6,{...i,ref:e})})})})}),R6=T.forwardRef((t,e)=>{const n=sa(ri,t.__scopeMenu),r=T.useRef(null),i=Pt(e,r);return T.useEffect(()=>{const s=r.current;if(s)return EE(s)},[]),w.jsx(wE,{...t,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,disableOutsideScroll:!0,onFocusOutside:je(t.onFocusOutside,s=>s.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>n.onOpenChange(!1)})}),I6=T.forwardRef((t,e)=>{const n=sa(ri,t.__scopeMenu);return w.jsx(wE,{...t,ref:e,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>n.onOpenChange(!1)})}),O6=Go("MenuContent.ScrollLock"),wE=T.forwardRef((t,e)=>{const{__scopeMenu:n,loop:r=!1,trapFocus:i,onOpenAutoFocus:s,onCloseAutoFocus:o,disableOutsidePointerEvents:l,onEntryFocus:c,onEscapeKeyDown:d,onPointerDownOutside:f,onFocusOutside:p,onInteractOutside:m,onDismiss:g,disableOutsideScroll:x,...v}=t,S=sa(ri,n),C=Oc(ri,n),A=Kh(n),k=uC(n),M=T6(n),[F,I]=T.useState(null),D=T.useRef(null),G=Pt(e,D,S.onContentChange),X=T.useRef(0),P=T.useRef(""),Y=T.useRef(0),z=T.useRef(null),ie=T.useRef("right"),Z=T.useRef(0),ee=x?Gh:T.Fragment,ae=x?{as:O6,allowPinchZoom:!0}:void 0,de=W=>{const O=P.current+W,U=M().filter(J=>!J.disabled),Q=document.activeElement,R=U.find(J=>J.ref.current===Q)?.textValue,oe=U.map(J=>J.textValue),pe=W6(oe,O,R),ue=U.find(J=>J.textValue===pe)?.ref.current;(function J(he){P.current=he,window.clearTimeout(X.current),he!==""&&(X.current=window.setTimeout(()=>J(""),1e3))})(O),ue&&setTimeout(()=>ue.focus())};T.useEffect(()=>()=>window.clearTimeout(X.current),[]),bE();const j=T.useCallback(W=>ie.current===z.current?.side&&G6(W,z.current?.area),[]);return w.jsx(N6,{scope:n,searchRef:P,onItemEnter:T.useCallback(W=>{j(W)&&W.preventDefault()},[j]),onItemLeave:T.useCallback(W=>{j(W)||(D.current?.focus(),I(null))},[j]),onTriggerLeave:T.useCallback(W=>{j(W)&&W.preventDefault()},[j]),pointerGraceTimerRef:Y,onPointerGraceIntentChange:T.useCallback(W=>{z.current=W},[]),children:w.jsx(ee,{...ae,children:w.jsx(Wh,{asChild:!0,trapped:i,onMountAutoFocus:je(s,W=>{W.preventDefault(),D.current?.focus({preventScroll:!0})}),onUnmountAutoFocus:o,children:w.jsx(kc,{asChild:!0,disableOutsidePointerEvents:l,onEscapeKeyDown:d,onPointerDownOutside:f,onFocusOutside:p,onInteractOutside:m,onDismiss:g,children:w.jsx(O3,{asChild:!0,...k,dir:C.dir,orientation:"vertical",loop:r,currentTabStopId:F,onCurrentTabStopIdChange:I,onEntryFocus:je(c,W=>{C.isUsingKeyboardRef.current||W.preventDefault()}),preventScrollOnEntryFocus:!0,children:w.jsx(fE,{role:"menu","aria-orientation":"vertical","data-state":NC(S.open),"data-radix-menu-content":"",dir:C.dir,...A,...v,ref:G,style:{outline:"none",...v.style},onKeyDown:je(v.onKeyDown,W=>{const U=W.target.closest("[data-radix-menu-content]")===W.currentTarget,Q=W.ctrlKey||W.altKey||W.metaKey,R=W.key.length===1;U&&(W.key==="Tab"&&W.preventDefault(),!Q&&R&&de(W.key));const oe=D.current;if(W.target!==oe||!x6.includes(W.key))return;W.preventDefault();const ue=M().filter(J=>!J.disabled).map(J=>J.ref.current);aC.includes(W.key)&&ue.reverse(),j6(ue)}),onBlur:je(t.onBlur,W=>{W.currentTarget.contains(W.target)||(window.clearTimeout(X.current),P.current="")}),onPointerMove:je(t.onPointerMove,cc(W=>{const O=W.target,U=Z.current!==W.clientX;if(W.currentTarget.contains(O)&&U){const Q=W.clientX>Z.current?"right":"left";ie.current=Q,Z.current=W.clientX}}))})})})})})})});hC.displayName=ri;var M6="MenuGroup",TE=T.forwardRef((t,e)=>{const{__scopeMenu:n,...r}=t;return w.jsx(xt.div,{role:"group",...r,ref:e})});TE.displayName=M6;var D6="MenuLabel",pC=T.forwardRef((t,e)=>{const{__scopeMenu:n,...r}=t;return w.jsx(xt.div,{...r,ref:e})});pC.displayName=D6;var th="MenuItem",Wv="menu.itemSelect",Yh=T.forwardRef((t,e)=>{const{disabled:n=!1,onSelect:r,...i}=t,s=T.useRef(null),o=Oc(th,t.__scopeMenu),l=vE(th,t.__scopeMenu),c=Pt(e,s),d=T.useRef(!1),f=()=>{const p=s.current;if(!n&&p){const m=new CustomEvent(Wv,{bubbles:!0,cancelable:!0});p.addEventListener(Wv,g=>r?.(g),{once:!0}),u_(p,m),m.defaultPrevented?d.current=!1:o.onClose()}};return w.jsx(mC,{...i,ref:c,disabled:n,onClick:je(t.onClick,f),onPointerDown:p=>{t.onPointerDown?.(p),d.current=!0},onPointerUp:je(t.onPointerUp,p=>{d.current||p.currentTarget?.click()}),onKeyDown:je(t.onKeyDown,p=>{const m=l.searchRef.current!=="";n||m&&p.key===" "||P0.includes(p.key)&&(p.currentTarget.click(),p.preventDefault())})})});Yh.displayName=th;var mC=T.forwardRef((t,e)=>{const{__scopeMenu:n,disabled:r=!1,textValue:i,...s}=t,o=vE(th,n),l=uC(n),c=T.useRef(null),d=Pt(e,c),[f,p]=T.useState(!1),[m,g]=T.useState("");return T.useEffect(()=>{const x=c.current;x&&g((x.textContent??"").trim())},[s.children]),w.jsx(uc.ItemSlot,{scope:n,disabled:r,textValue:i??m,children:w.jsx(M3,{asChild:!0,...l,focusable:!r,children:w.jsx(xt.div,{role:"menuitem","data-highlighted":f?"":void 0,"aria-disabled":r||void 0,"data-disabled":r?"":void 0,...s,ref:d,onPointerMove:je(t.onPointerMove,cc(x=>{r?o.onItemLeave(x):(o.onItemEnter(x),x.defaultPrevented||x.currentTarget.focus({preventScroll:!0}))})),onPointerLeave:je(t.onPointerLeave,cc(x=>o.onItemLeave(x))),onFocus:je(t.onFocus,()=>p(!0)),onBlur:je(t.onBlur,()=>p(!1))})})})}),L6="MenuCheckboxItem",gC=T.forwardRef((t,e)=>{const{checked:n=!1,onCheckedChange:r,...i}=t;return w.jsx(vC,{scope:t.__scopeMenu,checked:n,children:w.jsx(Yh,{role:"menuitemcheckbox","aria-checked":nh(n)?"mixed":n,...i,ref:e,"data-state":_E(n),onSelect:je(i.onSelect,()=>r?.(nh(n)?!0:!n),{checkForDefaultPrevented:!1})})})});gC.displayName=L6;var bC="MenuRadioGroup",[P6,F6]=ia(bC,{value:void 0,onValueChange:()=>{}}),EC=T.forwardRef((t,e)=>{const{value:n,onValueChange:r,...i}=t,s=Yi(r);return w.jsx(P6,{scope:t.__scopeMenu,value:n,onValueChange:s,children:w.jsx(TE,{...i,ref:e})})});EC.displayName=bC;var yC="MenuRadioItem",xC=T.forwardRef((t,e)=>{const{value:n,...r}=t,i=F6(yC,t.__scopeMenu),s=n===i.value;return w.jsx(vC,{scope:t.__scopeMenu,checked:s,children:w.jsx(Yh,{role:"menuitemradio","aria-checked":s,...r,ref:e,"data-state":_E(s),onSelect:je(r.onSelect,()=>i.onValueChange?.(n),{checkForDefaultPrevented:!1})})})});xC.displayName=yC;var SE="MenuItemIndicator",[vC,B6]=ia(SE,{checked:!1}),wC=T.forwardRef((t,e)=>{const{__scopeMenu:n,forceMount:r,...i}=t,s=B6(SE,n);return w.jsx(Zi,{present:r||nh(s.checked)||s.checked===!0,children:w.jsx(xt.span,{...i,ref:e,"data-state":_E(s.checked)})})});wC.displayName=SE;var U6="MenuSeparator",TC=T.forwardRef((t,e)=>{const{__scopeMenu:n,...r}=t;return w.jsx(xt.div,{role:"separator","aria-orientation":"horizontal",...r,ref:e})});TC.displayName=U6;var H6="MenuArrow",SC=T.forwardRef((t,e)=>{const{__scopeMenu:n,...r}=t,i=Kh(n);return w.jsx(hE,{...i,...r,ref:e})});SC.displayName=H6;var z6="MenuSub",[UX,_C]=ia(z6),Fu="MenuSubTrigger",CC=T.forwardRef((t,e)=>{const n=sa(Fu,t.__scopeMenu),r=Oc(Fu,t.__scopeMenu),i=_C(Fu,t.__scopeMenu),s=vE(Fu,t.__scopeMenu),o=T.useRef(null),{pointerGraceTimerRef:l,onPointerGraceIntentChange:c}=s,d={__scopeMenu:t.__scopeMenu},f=T.useCallback(()=>{o.current&&window.clearTimeout(o.current),o.current=null},[]);return T.useEffect(()=>f,[f]),T.useEffect(()=>{const p=l.current;return()=>{window.clearTimeout(p),c(null)}},[l,c]),w.jsx(yE,{asChild:!0,...d,children:w.jsx(mC,{id:i.triggerId,"aria-haspopup":"menu","aria-expanded":n.open,"aria-controls":i.contentId,"data-state":NC(n.open),...t,ref:Ph(e,i.onTriggerChange),onClick:p=>{t.onClick?.(p),!(t.disabled||p.defaultPrevented)&&(p.currentTarget.focus(),n.open||n.onOpenChange(!0))},onPointerMove:je(t.onPointerMove,cc(p=>{s.onItemEnter(p),!p.defaultPrevented&&!t.disabled&&!n.open&&!o.current&&(s.onPointerGraceIntentChange(null),o.current=window.setTimeout(()=>{n.onOpenChange(!0),f()},100))})),onPointerLeave:je(t.onPointerLeave,cc(p=>{f();const m=n.content?.getBoundingClientRect();if(m){const g=n.content?.dataset.side,x=g==="right",v=x?-5:5,S=m[x?"left":"right"],C=m[x?"right":"left"];s.onPointerGraceIntentChange({area:[{x:p.clientX+v,y:p.clientY},{x:S,y:m.top},{x:C,y:m.top},{x:C,y:m.bottom},{x:S,y:m.bottom}],side:g}),window.clearTimeout(l.current),l.current=window.setTimeout(()=>s.onPointerGraceIntentChange(null),300)}else{if(s.onTriggerLeave(p),p.defaultPrevented)return;s.onPointerGraceIntentChange(null)}})),onKeyDown:je(t.onKeyDown,p=>{const m=s.searchRef.current!=="";t.disabled||m&&p.key===" "||v6[r.dir].includes(p.key)&&(n.onOpenChange(!0),n.content?.focus(),p.preventDefault())})})})});CC.displayName=Fu;var AC="MenuSubContent",kC=T.forwardRef((t,e)=>{const n=dC(ri,t.__scopeMenu),{forceMount:r=n.forceMount,...i}=t,s=sa(ri,t.__scopeMenu),o=Oc(ri,t.__scopeMenu),l=_C(AC,t.__scopeMenu),c=T.useRef(null),d=Pt(e,c);return w.jsx(uc.Provider,{scope:t.__scopeMenu,children:w.jsx(Zi,{present:r||s.open,children:w.jsx(uc.Slot,{scope:t.__scopeMenu,children:w.jsx(wE,{id:l.contentId,"aria-labelledby":l.triggerId,...i,ref:d,align:"start",side:o.dir==="rtl"?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:f=>{o.isUsingKeyboardRef.current&&c.current?.focus(),f.preventDefault()},onCloseAutoFocus:f=>f.preventDefault(),onFocusOutside:je(t.onFocusOutside,f=>{f.target!==l.trigger&&s.onOpenChange(!1)}),onEscapeKeyDown:je(t.onEscapeKeyDown,f=>{o.onClose(),f.preventDefault()}),onKeyDown:je(t.onKeyDown,f=>{const p=f.currentTarget.contains(f.target),m=w6[o.dir].includes(f.key);p&&m&&(s.onOpenChange(!1),l.trigger?.focus(),f.preventDefault())})})})})})});kC.displayName=AC;function NC(t){return t?"open":"closed"}function nh(t){return t==="indeterminate"}function _E(t){return nh(t)?"indeterminate":t?"checked":"unchecked"}function j6(t){const e=document.activeElement;for(const n of t)if(n===e||(n.focus(),document.activeElement!==e))return}function $6(t,e){return t.map((n,r)=>t[(e+r)%t.length])}function W6(t,e,n){const i=e.length>1&&Array.from(e).every(d=>d===e[0])?e[0]:e,s=n?t.indexOf(n):-1;let o=$6(t,Math.max(s,0));i.length===1&&(o=o.filter(d=>d!==n));const c=o.find(d=>d.toLowerCase().startsWith(i.toLowerCase()));return c!==n?c:void 0}function V6(t,e){const{x:n,y:r}=t;let i=!1;for(let s=0,o=e.length-1;sr!=m>r&&n<(p-d)*(r-f)/(m-f)+d&&(i=!i)}return i}function G6(t,e){if(!e)return!1;const n={x:t.clientX,y:t.clientY};return V6(n,e)}function cc(t){return e=>e.pointerType==="mouse"?t(e):void 0}var K6=cC,Y6=yE,q6=fC,X6=hC,Q6=TE,Z6=pC,J6=Yh,e4=gC,t4=EC,n4=xC,r4=wC,i4=TC,s4=SC,o4=CC,a4=kC,qh="DropdownMenu",[l4,HX]=Cs(qh,[lC]),xr=lC(),[u4,RC]=l4(qh),IC=t=>{const{__scopeDropdownMenu:e,children:n,dir:r,open:i,defaultOpen:s,onOpenChange:o,modal:l=!0}=t,c=xr(e),d=T.useRef(null),[f,p]=Yo({prop:i,defaultProp:s??!1,onChange:o,caller:qh});return w.jsx(u4,{scope:e,triggerId:Gi(),triggerRef:d,contentId:Gi(),open:f,onOpenChange:p,onOpenToggle:T.useCallback(()=>p(m=>!m),[p]),modal:l,children:w.jsx(K6,{...c,open:f,onOpenChange:p,dir:r,modal:l,children:n})})};IC.displayName=qh;var OC="DropdownMenuTrigger",MC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,disabled:r=!1,...i}=t,s=RC(OC,n),o=xr(n);return w.jsx(Y6,{asChild:!0,...o,children:w.jsx(xt.button,{type:"button",id:s.triggerId,"aria-haspopup":"menu","aria-expanded":s.open,"aria-controls":s.open?s.contentId:void 0,"data-state":s.open?"open":"closed","data-disabled":r?"":void 0,disabled:r,...i,ref:Ph(e,s.triggerRef),onPointerDown:je(t.onPointerDown,l=>{!r&&l.button===0&&l.ctrlKey===!1&&(s.onOpenToggle(),s.open||l.preventDefault())}),onKeyDown:je(t.onKeyDown,l=>{r||(["Enter"," "].includes(l.key)&&s.onOpenToggle(),l.key==="ArrowDown"&&s.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(l.key)&&l.preventDefault())})})})});MC.displayName=OC;var c4="DropdownMenuPortal",DC=t=>{const{__scopeDropdownMenu:e,...n}=t,r=xr(e);return w.jsx(q6,{...r,...n})};DC.displayName=c4;var LC="DropdownMenuContent",PC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=RC(LC,n),s=xr(n),o=T.useRef(!1);return w.jsx(X6,{id:i.contentId,"aria-labelledby":i.triggerId,...s,...r,ref:e,onCloseAutoFocus:je(t.onCloseAutoFocus,l=>{o.current||i.triggerRef.current?.focus(),o.current=!1,l.preventDefault()}),onInteractOutside:je(t.onInteractOutside,l=>{const c=l.detail.originalEvent,d=c.button===0&&c.ctrlKey===!0,f=c.button===2||d;(!i.modal||f)&&(o.current=!0)}),style:{...t.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})});PC.displayName=LC;var d4="DropdownMenuGroup",f4=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(Q6,{...i,...r,ref:e})});f4.displayName=d4;var h4="DropdownMenuLabel",FC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(Z6,{...i,...r,ref:e})});FC.displayName=h4;var p4="DropdownMenuItem",BC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(J6,{...i,...r,ref:e})});BC.displayName=p4;var m4="DropdownMenuCheckboxItem",UC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(e4,{...i,...r,ref:e})});UC.displayName=m4;var g4="DropdownMenuRadioGroup",b4=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(t4,{...i,...r,ref:e})});b4.displayName=g4;var E4="DropdownMenuRadioItem",HC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(n4,{...i,...r,ref:e})});HC.displayName=E4;var y4="DropdownMenuItemIndicator",zC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(r4,{...i,...r,ref:e})});zC.displayName=y4;var x4="DropdownMenuSeparator",jC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(i4,{...i,...r,ref:e})});jC.displayName=x4;var v4="DropdownMenuArrow",w4=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(s4,{...i,...r,ref:e})});w4.displayName=v4;var T4="DropdownMenuSubTrigger",$C=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(o4,{...i,...r,ref:e})});$C.displayName=T4;var S4="DropdownMenuSubContent",WC=T.forwardRef((t,e)=>{const{__scopeDropdownMenu:n,...r}=t,i=xr(n);return w.jsx(a4,{...i,...r,ref:e,style:{...t.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})});WC.displayName=S4;var _4=IC,C4=MC,A4=DC,VC=PC,GC=FC,KC=BC,YC=UC,qC=HC,XC=zC,QC=jC,ZC=$C,JC=WC;/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const k4=t=>t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),eA=(...t)=>t.filter((e,n,r)=>!!e&&r.indexOf(e)===n).join(" ");/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */var N4={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const R4=T.forwardRef(({color:t="currentColor",size:e=24,strokeWidth:n=2,absoluteStrokeWidth:r,className:i="",children:s,iconNode:o,...l},c)=>T.createElement("svg",{ref:c,...N4,width:e,height:e,stroke:t,strokeWidth:r?Number(n)*24/Number(e):n,className:eA("lucide",i),...l},[...o.map(([d,f])=>T.createElement(d,f)),...Array.isArray(s)?s:[s]]));/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Kr=(t,e)=>{const n=T.forwardRef(({className:r,...i},s)=>T.createElement(R4,{ref:s,iconNode:e,className:eA(`lucide-${k4(t)}`,r),...i}));return n.displayName=`${t}`,n};/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const CE=Kr("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const tA=Kr("ChevronDown",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const I4=Kr("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const O4=Kr("ChevronUp",[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const M4=Kr("Circle",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const D4=Kr("EyeOff",[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",key:"ct8e1f"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242",key:"151rxh"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143",key:"13bj9a"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const L4=Kr("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const nA=Kr("Flag",[["path",{d:"M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z",key:"i9b6wo"}],["line",{x1:"4",x2:"4",y1:"22",y2:"15",key:"1cm3nv"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const P4=Kr("Menu",[["line",{x1:"4",x2:"20",y1:"12",y2:"12",key:"1e0a9i"}],["line",{x1:"4",x2:"20",y1:"6",y2:"6",key:"1owob3"}],["line",{x1:"4",x2:"20",y1:"18",y2:"18",key:"yk5zj1"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const F4=Kr("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const B4=Kr("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const Vv=Kr("ShieldEllipsis",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M8 12h.01",key:"czm47f"}],["path",{d:"M12 12h.01",key:"1mp3jc"}],["path",{d:"M16 12h.01",key:"1l6xoz"}]]);/** * @license lucide-react v0.453.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */const gl=Kr("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]),rA=_4,iA=C4,U4=T.forwardRef(({className:t,inset:e,children:n,...r},i)=>w.jsxs(ZC,{ref:i,className:Rt("flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",e&&"pl-8",t),...r,children:[n,w.jsx(I4,{className:"ml-auto h-4 w-4"})]}));U4.displayName=ZC.displayName;const H4=T.forwardRef(({className:t,...e},n)=>w.jsx(JC,{ref:n,className:Rt("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",t),...e}));H4.displayName=JC.displayName;const AE=T.forwardRef(({className:t,sideOffset:e=4,...n},r)=>w.jsx(A4,{children:w.jsx(VC,{ref:r,sideOffset:e,className:Rt("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",t),...n})}));AE.displayName=VC.displayName;const rh=T.forwardRef(({className:t,inset:e,...n},r)=>w.jsx(KC,{ref:r,className:Rt("relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",e&&"pl-8",t),...n}));rh.displayName=KC.displayName;const z4=T.forwardRef(({className:t,children:e,checked:n,...r},i)=>w.jsxs(YC,{ref:i,className:Rt("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",t),checked:n,...r,children:[w.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:w.jsx(XC,{children:w.jsx(CE,{className:"h-4 w-4"})})}),e]}));z4.displayName=YC.displayName;const j4=T.forwardRef(({className:t,children:e,...n},r)=>w.jsxs(qC,{ref:r,className:Rt("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",t),...n,children:[w.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:w.jsx(XC,{children:w.jsx(M4,{className:"h-2 w-2 fill-current"})})}),e]}));j4.displayName=qC.displayName;const $4=T.forwardRef(({className:t,inset:e,...n},r)=>w.jsx(GC,{ref:r,className:Rt("px-2 py-1.5 text-sm font-semibold",e&&"pl-8",t),...n}));$4.displayName=GC.displayName;const W4=T.forwardRef(({className:t,...e},n)=>w.jsx(QC,{ref:n,className:Rt("-mx-1 my-1 h-px bg-muted",t),...e}));W4.displayName=QC.displayName;const sA=t=>{t=new Date(t);const e={year:"numeric",month:"long",day:"numeric"};return t.toLocaleDateString("en-US",e)},V4=t=>{t=new Date(t);const e={hour:"2-digit",minute:"2-digit",hour12:!1};return t.toLocaleTimeString("en-US",e)},G4="_editSession_1nfqv_1",K4={editSession:G4},vs=t=>{(t.key==="Enter"||t.key===" ")&&t.target.click()};function Y4(t){const e=t.getBoundingClientRect();return window.innerWidth-e.right}var Xh="Dialog",[oA,zX]=Cs(Xh),[q4,Ai]=oA(Xh),aA=t=>{const{__scopeDialog:e,children:n,open:r,defaultOpen:i,onOpenChange:s,modal:o=!0}=t,l=T.useRef(null),c=T.useRef(null),[d,f]=Yo({prop:r,defaultProp:i??!1,onChange:s,caller:Xh});return w.jsx(q4,{scope:e,triggerRef:l,contentRef:c,contentId:Gi(),titleId:Gi(),descriptionId:Gi(),open:d,onOpenChange:f,onOpenToggle:T.useCallback(()=>f(p=>!p),[f]),modal:o,children:n})};aA.displayName=Xh;var lA="DialogTrigger",uA=T.forwardRef((t,e)=>{const{__scopeDialog:n,...r}=t,i=Ai(lA,n),s=Pt(e,i.triggerRef);return w.jsx(xt.button,{type:"button","aria-haspopup":"dialog","aria-expanded":i.open,"aria-controls":i.contentId,"data-state":OE(i.open),...r,ref:s,onClick:je(t.onClick,i.onOpenToggle)})});uA.displayName=lA;var kE="DialogPortal",[X4,cA]=oA(kE,{forceMount:void 0}),dA=t=>{const{__scopeDialog:e,forceMount:n,children:r,container:i}=t,s=Ai(kE,e);return w.jsx(X4,{scope:e,forceMount:n,children:T.Children.map(r,o=>w.jsx(Zi,{present:n||s.open,children:w.jsx(Hh,{asChild:!0,container:i,children:o})}))})};dA.displayName=kE;var ih="DialogOverlay",fA=T.forwardRef((t,e)=>{const n=cA(ih,t.__scopeDialog),{forceMount:r=n.forceMount,...i}=t,s=Ai(ih,t.__scopeDialog);return s.modal?w.jsx(Zi,{present:r||s.open,children:w.jsx(Z4,{...i,ref:e})}):null});fA.displayName=ih;var Q4=Go("DialogOverlay.RemoveScroll"),Z4=T.forwardRef((t,e)=>{const{__scopeDialog:n,...r}=t,i=Ai(ih,n);return w.jsx(Gh,{as:Q4,allowPinchZoom:!0,shards:[i.contentRef],children:w.jsx(xt.div,{"data-state":OE(i.open),...r,ref:e,style:{pointerEvents:"auto",...r.style}})})}),qo="DialogContent",hA=T.forwardRef((t,e)=>{const n=cA(qo,t.__scopeDialog),{forceMount:r=n.forceMount,...i}=t,s=Ai(qo,t.__scopeDialog);return w.jsx(Zi,{present:r||s.open,children:s.modal?w.jsx(J4,{...i,ref:e}):w.jsx(eF,{...i,ref:e})})});hA.displayName=qo;var J4=T.forwardRef((t,e)=>{const n=Ai(qo,t.__scopeDialog),r=T.useRef(null),i=Pt(e,n.contentRef,r);return T.useEffect(()=>{const s=r.current;if(s)return EE(s)},[]),w.jsx(pA,{...t,ref:i,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:je(t.onCloseAutoFocus,s=>{s.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:je(t.onPointerDownOutside,s=>{const o=s.detail.originalEvent,l=o.button===0&&o.ctrlKey===!0;(o.button===2||l)&&s.preventDefault()}),onFocusOutside:je(t.onFocusOutside,s=>s.preventDefault())})}),eF=T.forwardRef((t,e)=>{const n=Ai(qo,t.__scopeDialog),r=T.useRef(!1),i=T.useRef(!1);return w.jsx(pA,{...t,ref:e,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:s=>{t.onCloseAutoFocus?.(s),s.defaultPrevented||(r.current||n.triggerRef.current?.focus(),s.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:s=>{t.onInteractOutside?.(s),s.defaultPrevented||(r.current=!0,s.detail.originalEvent.type==="pointerdown"&&(i.current=!0));const o=s.target;n.triggerRef.current?.contains(o)&&s.preventDefault(),s.detail.originalEvent.type==="focusin"&&i.current&&s.preventDefault()}})}),pA=T.forwardRef((t,e)=>{const{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:s,...o}=t,l=Ai(qo,n),c=T.useRef(null),d=Pt(e,c);return bE(),w.jsxs(w.Fragment,{children:[w.jsx(Wh,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:s,children:w.jsx(kc,{role:"dialog",id:l.contentId,"aria-describedby":l.descriptionId,"aria-labelledby":l.titleId,"data-state":OE(l.open),...o,ref:d,onDismiss:()=>l.onOpenChange(!1)})}),w.jsxs(w.Fragment,{children:[w.jsx(tF,{titleId:l.titleId}),w.jsx(rF,{contentRef:c,descriptionId:l.descriptionId})]})]})}),NE="DialogTitle",RE=T.forwardRef((t,e)=>{const{__scopeDialog:n,...r}=t,i=Ai(NE,n);return w.jsx(xt.h2,{id:i.titleId,...r,ref:e})});RE.displayName=NE;var mA="DialogDescription",IE=T.forwardRef((t,e)=>{const{__scopeDialog:n,...r}=t,i=Ai(mA,n);return w.jsx(xt.p,{id:i.descriptionId,...r,ref:e})});IE.displayName=mA;var gA="DialogClose",bA=T.forwardRef((t,e)=>{const{__scopeDialog:n,...r}=t,i=Ai(gA,n);return w.jsx(xt.button,{type:"button",...r,ref:e,onClick:je(t.onClick,()=>i.onOpenChange(!1))})});bA.displayName=gA;function OE(t){return t?"open":"closed"}var EA="DialogTitleWarning",[jX,yA]=JD(EA,{contentName:qo,titleName:NE,docsSlug:"dialog"}),tF=({titleId:t})=>{const e=yA(EA),n=`\`${e.contentName}\` requires a \`${e.titleName}\` for the component to be accessible for screen reader users. If you want to hide the \`${e.titleName}\`, you can wrap it with our VisuallyHidden component. For more information, see https://radix-ui.com/primitives/docs/components/${e.docsSlug}`;return T.useEffect(()=>{t&&(document.getElementById(t)||console.error(n))},[n,t]),null},nF="DialogDescriptionWarning",rF=({contentRef:t,descriptionId:e})=>{const r=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${yA(nF).contentName}}.`;return T.useEffect(()=>{const i=t.current?.getAttribute("aria-describedby");e&&i&&(document.getElementById(e)||console.warn(r))},[r,t,e]),null},xA=aA,vA=uA,wA=dA,Qh=fA,Zh=hA,Jh=RE,ep=IE,ME=bA;const TA=xA,iF=vA,DE=wA,Gv=ME,SA=T.forwardRef(({className:t,...e},n)=>w.jsx(Qh,{ref:n,className:Rt("fixed inset-0 bg-black/80 z-[99] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",t),...e}));SA.displayName=Qh.displayName;const LE=T.forwardRef(({className:t,children:e,...n},r)=>w.jsxs(DE,{children:[w.jsx(SA,{}),w.jsxs(Zh,{ref:r,className:Rt("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",t),...n,children:[e,w.jsxs(ME,{className:"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground",children:[w.jsx(gl,{className:"h-4 w-4"}),w.jsx("span",{className:"sr-only",children:"Close"})]})]})]}));LE.displayName=Zh.displayName;const PE=({className:t,...e})=>w.jsx("div",{className:Rt("flex flex-col space-y-1.5 text-center sm:text-left",t),...e});PE.displayName="DialogHeader";const FE=T.forwardRef(({className:t,...e},n)=>w.jsx(Jh,{ref:n,className:Rt("text-lg font-semibold leading-none tracking-tight",t),...e}));FE.displayName=Jh.displayName;const BE=T.forwardRef(({className:t,...e},n)=>w.jsx(ep,{ref:n,className:Rt("text-sm text-muted-foreground",t),...e}));BE.displayName=ep.displayName;const Bu={},Kv=(t,e)=>t.unstable_is?t.unstable_is(e):e===t,Yv=t=>"init"in t,_g=t=>!!t.write,qv=t=>"v"in t||"e"in t,af=t=>{if("e"in t)throw t.e;if((Bu?"production":void 0)!=="production"&&!("v"in t))throw new Error("[Bug] atom state is not initialized");return t.v},sh=new WeakMap,Xv=t=>{var e;return oh(t)&&!!((e=sh.get(t))!=null&&e[0])},sF=t=>{const e=sh.get(t);e?.[0]&&(e[0]=!1,e[1].forEach(n=>n()))},_A=(t,e)=>{let n=sh.get(t);if(!n){n=[!0,new Set],sh.set(t,n);const r=()=>{n[0]=!1};t.then(r,r)}n[1].add(e)},oh=t=>typeof t?.then=="function",CA=(t,e,n)=>{n.p.has(t)||(n.p.add(t),e.then(()=>{n.p.delete(t)},()=>{n.p.delete(t)}))},Cg=(t,e,n)=>{const r=n(t),i="v"in r,s=r.v;if(oh(e))for(const o of r.d.keys())CA(t,e,n(o));r.v=e,delete r.e,(!i||!Object.is(s,r.v))&&(++r.n,oh(s)&&sF(s))},Qv=(t,e,n)=>{var r;const i=new Set;for(const s of((r=n.get(t))==null?void 0:r.t)||[])n.has(s)&&i.add(s);for(const s of e.p)i.add(s);return i},oF=()=>{const t=new Set,e=()=>{t.forEach(n=>n())};return e.add=n=>(t.add(n),()=>{t.delete(n)}),e},Ag=()=>{const t={},e=new WeakMap,n=r=>{var i,s;(i=e.get(t))==null||i.forEach(o=>o(r)),(s=e.get(r))==null||s.forEach(o=>o())};return n.add=(r,i)=>{const s=r||t,o=(e.has(s)?e:e.set(s,new Set)).get(s);return o.add(i),()=>{o?.delete(i),o.size||e.delete(s)}},n},aF=t=>(t.c||(t.c=Ag()),t.m||(t.m=Ag()),t.u||(t.u=Ag()),t.f||(t.f=oF()),t),lF=Symbol(),uF=(t=new WeakMap,e=new WeakMap,n=new WeakMap,r=new Set,i=new Set,s=new Set,o={},l=(m,...g)=>m.read(...g),c=(m,...g)=>m.write(...g),d=(m,g)=>{var x;return(x=m.unstable_onInit)==null?void 0:x.call(m,g)},f=(m,g)=>{var x;return(x=m.onMount)==null?void 0:x.call(m,g)},...p)=>{const m=p[0]||(D=>{if((Bu?"production":void 0)!=="production"&&!D)throw new Error("Atom is undefined or null");let G=t.get(D);return G||(G={d:new Map,p:new Set,n:0},t.set(D,G),d?.(D,I)),G}),g=p[1]||(()=>{const D=[],G=X=>{try{X()}catch(P){D.push(P)}};do{o.f&&G(o.f);const X=new Set,P=X.add.bind(X);r.forEach(Y=>{var z;return(z=e.get(Y))==null?void 0:z.l.forEach(P)}),r.clear(),s.forEach(P),s.clear(),i.forEach(P),i.clear(),X.forEach(G),r.size&&x()}while(r.size||s.size||i.size);if(D.length)throw new AggregateError(D)}),x=p[2]||(()=>{const D=[],G=new WeakSet,X=new WeakSet,P=Array.from(r);for(;P.length;){const Y=P[P.length-1],z=m(Y);if(X.has(Y)){P.pop();continue}if(G.has(Y)){if(n.get(Y)===z.n)D.push([Y,z]);else if((Bu?"production":void 0)!=="production"&&n.has(Y))throw new Error("[Bug] invalidated atom exists");X.add(Y),P.pop();continue}G.add(Y);for(const ie of Qv(Y,z,e))G.has(ie)||P.push(ie)}for(let Y=D.length-1;Y>=0;--Y){const[z,ie]=D[Y];let Z=!1;for(const ee of ie.d.keys())if(ee!==z&&r.has(ee)){Z=!0;break}Z&&(v(z),A(z)),n.delete(z)}}),v=p[3]||(D=>{var G;const X=m(D);if(qv(X)&&(e.has(D)&&n.get(D)!==X.n||Array.from(X.d).every(([de,j])=>v(de).n===j)))return X;X.d.clear();let P=!0;const Y=()=>{e.has(D)&&(A(D),x(),g())},z=de=>{var j;if(Kv(D,de)){const O=m(de);if(!qv(O))if(Yv(de))Cg(de,de.init,m);else throw new Error("no atom init");return af(O)}const W=v(de);try{return af(W)}finally{X.d.set(de,W.n),Xv(X.v)&&CA(D,X.v,W),(j=e.get(de))==null||j.t.add(D),P||Y()}};let ie,Z;const ee={get signal(){return ie||(ie=new AbortController),ie.signal},get setSelf(){return(Bu?"production":void 0)!=="production"&&!_g(D)&&console.warn("setSelf function cannot be used with read-only atom"),!Z&&_g(D)&&(Z=(...de)=>{if((Bu?"production":void 0)!=="production"&&P&&console.warn("setSelf function cannot be called in sync"),!P)try{return C(D,...de)}finally{x(),g()}}),Z}},ae=X.n;try{const de=l(D,z,ee);return Cg(D,de,m),oh(de)&&(_A(de,()=>ie?.abort()),de.then(Y,Y)),X}catch(de){return delete X.v,X.e=de,++X.n,X}finally{P=!1,ae!==X.n&&n.get(D)===ae&&(n.set(D,X.n),r.add(D),(G=o.c)==null||G.call(o,D))}}),S=p[4]||(D=>{const G=[D];for(;G.length;){const X=G.pop(),P=m(X);for(const Y of Qv(X,P,e)){const z=m(Y);n.set(Y,z.n),G.push(Y)}}}),C=p[5]||((D,...G)=>{let X=!0;const P=z=>af(v(z)),Y=(z,...ie)=>{var Z;const ee=m(z);try{if(Kv(D,z)){if(!Yv(z))throw new Error("atom not writable");const ae=ee.n,de=ie[0];Cg(z,de,m),A(z),ae!==ee.n&&(r.add(z),(Z=o.c)==null||Z.call(o,z),S(z));return}else return C(z,...ie)}finally{X||(x(),g())}};try{return c(D,P,Y,...G)}finally{X=!1}}),A=p[6]||(D=>{var G;const X=m(D),P=e.get(D);if(P&&!Xv(X.v)){for(const[Y,z]of X.d)if(!P.d.has(Y)){const ie=m(Y);k(Y).t.add(D),P.d.add(Y),z!==ie.n&&(r.add(Y),(G=o.c)==null||G.call(o,Y),S(Y))}for(const Y of P.d||[])if(!X.d.has(Y)){P.d.delete(Y);const z=M(Y);z?.t.delete(D)}}}),k=p[7]||(D=>{var G;const X=m(D);let P=e.get(D);if(!P){v(D);for(const Y of X.d.keys())k(Y).t.add(D);if(P={l:new Set,d:new Set(X.d.keys()),t:new Set},e.set(D,P),(G=o.m)==null||G.call(o,D),_g(D)){const Y=()=>{let z=!0;const ie=(...Z)=>{try{return C(D,...Z)}finally{z||(x(),g())}};try{const Z=f(D,ie);Z&&(P.u=()=>{z=!0;try{Z()}finally{z=!1}})}finally{z=!1}};i.add(Y)}}return P}),M=p[8]||(D=>{var G;const X=m(D);let P=e.get(D);if(P&&!P.l.size&&!Array.from(P.t).some(Y=>{var z;return(z=e.get(Y))==null?void 0:z.d.has(D)})){P.u&&s.add(P.u),P=void 0,e.delete(D),(G=o.u)==null||G.call(o,D);for(const Y of X.d.keys()){const z=M(Y);z?.t.delete(D)}return}return P}),F=[t,e,n,r,i,s,o,l,c,d,f,m,g,x,v,S,C,A,k,M],I={get:D=>af(v(D)),set:(D,...G)=>{try{return C(D,...G)}finally{x(),g()}},sub:(D,G)=>{const P=k(D).l;return P.add(G),g(),()=>{P.delete(G),M(D),g()}}};return Object.defineProperty(I,lF,{value:F}),I},AA=uF,cF=aF,Zv=_A,UE={};let dF=0;function ki(t,e){const n=`atom${++dF}`,r={toString(){return(UE?"production":void 0)!=="production"&&this.debugLabel?n+":"+this.debugLabel:n}};return typeof t=="function"?r.read=t:(r.init=t,r.read=fF,r.write=hF),r}function fF(t){return t(this)}function hF(t,e,n){return e(this,typeof n=="function"?n(t(this)):n)}const pF=()=>{let t=0;const e=cF({}),n=new WeakMap,r=new WeakMap,i=AA(n,r,void 0,void 0,void 0,void 0,e,void 0,(l,c,d,...f)=>t?d(l,...f):l.write(c,d,...f)),s=new Set;return e.m.add(void 0,l=>{s.add(l);const c=n.get(l);c.m=r.get(l)}),e.u.add(void 0,l=>{s.delete(l);const c=n.get(l);delete c.m}),Object.assign(i,{dev4_get_internal_weak_map:()=>(console.log("Deprecated: Use devstore from the devtools library"),n),dev4_get_mounted_atoms:()=>s,dev4_restore_atoms:l=>{const c={read:()=>null,write:(d,f)=>{++t;try{for(const[p,m]of l)"init"in p&&f(p,m)}finally{--t}}};i.set(c)}})};function mF(){return(UE?"production":void 0)!=="production"?pF():AA()}let _u;function gF(){return _u||(_u=mF(),(UE?"production":void 0)!=="production"&&(globalThis.__JOTAI_DEFAULT_STORE__||(globalThis.__JOTAI_DEFAULT_STORE__=_u),globalThis.__JOTAI_DEFAULT_STORE__!==_u&&console.warn("Detected multiple Jotai instances. It may cause unexpected behavior with the default store. https://github.com/pmndrs/jotai/discussions/2044"))),_u}const bF={},EF=T.createContext(void 0);function kA(t){return T.useContext(EF)||gF()}const F0=t=>typeof t?.then=="function",B0=t=>{t.status||(t.status="pending",t.then(e=>{t.status="fulfilled",t.value=e},e=>{t.status="rejected",t.reason=e}))},yF=we.use||(t=>{if(t.status==="pending")throw t;if(t.status==="fulfilled")return t.value;throw t.status==="rejected"?t.reason:(B0(t),t)}),kg=new WeakMap,Jv=(t,e)=>{let n=kg.get(t);return n||(n=new Promise((r,i)=>{let s=t;const o=d=>f=>{s===d&&r(f)},l=d=>f=>{s===d&&i(f)},c=()=>{try{const d=e();F0(d)?(kg.set(d,n),s=d,d.then(o(d),l(d)),Zv(d,c)):r(d)}catch(d){i(d)}};t.then(o(t),l(t)),Zv(t,c)}),kg.set(t,n)),n};function xF(t,e){const{delay:n,unstable_promiseStatus:r=!we.use}={},i=kA(),[[s,o,l],c]=T.useReducer(f=>{const p=i.get(t);return Object.is(f[0],p)&&f[1]===i&&f[2]===t?f:[p,i,t]},void 0,()=>[i.get(t),i,t]);let d=s;if((o!==i||l!==t)&&(c(),d=i.get(t)),T.useEffect(()=>{const f=i.sub(t,()=>{if(r)try{const p=i.get(t);F0(p)&&B0(Jv(p,()=>i.get(t)))}catch{}if(typeof n=="number"){setTimeout(c,n);return}c()});return c(),f},[i,t,n,r]),T.useDebugValue(d),F0(d)){const f=Jv(d,()=>i.get(t));return r&&B0(f),yF(f)}return d}function vF(t,e){const n=kA();return T.useCallback((...i)=>{if((bF?"production":void 0)!=="production"&&!("write"in t))throw new Error("not writable atom");return n.set(t,...i)},[n,t])}function lt(t,e){return[xF(t),vF(t)]}const NA=()=>({kind:"message",source:"customer",creation_utc:new Date,serverStatus:"pending",offset:0,trace_id:"",data:{message:""}}),wF=()=>{try{return JSON.parse(localStorage.logs||"{}")}catch(t){return console.error(t),localStorage.removeItem("logs"),{}}};ki(wF());const tp=ki([]),HE=ki([]),np=ki(null),As=ki(null),oa=ki(null),zE=ki(null),rp=ki([]),TF=ki(null),SF=ki(NA()),ws=ki({closeDialog:()=>null,openDialog:()=>null}),Ft={green:{dark:"rgb(80 130 1)",light:"rgb(80 130 1 / 10%)",extraLight:"rgb(80 130 1 / 5%)"},purple:{dark:"rgb(85 1 104)",light:"rgb(85 1 104 / 10%)",extraLight:"rgb(85 1 104 / 5%)"},pink:{dark:"rgb(155 3 95)",light:"rgb(155 3 95 / 10%)",extraLight:"rgb(155 3 95 / 5%)"},orange:{dark:"rgb(183 99 0)",light:"rgb(183 99 0 / 10%)",extraLight:"rgb(183 99 0 / 5%)"},blue:{dark:"rgb(46 128 108)",light:"rgb(46 128 108 / 10%)",extraLight:"rgb(46 128 108 / 5%)"}},_F=[{text:"white",background:Ft.green.dark,outerBackground:Ft.green.light},{text:"white",background:Ft.purple.dark,outerBackground:Ft.purple.light},{text:"white",background:Ft.pink.dark,outerBackground:Ft.pink.light},{text:"white",background:Ft.orange.dark,outerBackground:Ft.orange.light},{text:"white",background:Ft.blue.dark,outerBackground:Ft.blue.light}],CF=[{iconBackground:Ft.green.dark,background:Ft.green.light,text:Ft.green.dark,outerBackground:Ft.green.extraLight},{iconBackground:Ft.purple.dark,background:Ft.purple.light,text:Ft.purple.dark,outerBackground:Ft.purple.extraLight},{iconBackground:Ft.pink.dark,background:Ft.pink.light,text:Ft.pink.dark,outerBackground:Ft.pink.extraLight},{iconBackground:Ft.orange.dark,background:Ft.orange.light,text:Ft.orange.dark,outerBackground:Ft.orange.extraLight},{iconBackground:Ft.blue.dark,background:Ft.blue.light,text:Ft.blue.dark,outerBackground:Ft.blue.extraLight}],U0=(t,e)=>{const n=e==="agent"?_F:CF,r=[...t].reduce((i,s)=>i+s.charCodeAt(0),0);return n[r%n.length]},H0=({agent:t,customer:e,tooltip:n=!0})=>{const r=U0(t.id,"agent"),i=e&&U0(e.id,"customer"),s=t?.name==="N/A",o=e?.name==="N/A",l=t.name.replaceAll(/>|{const[,t]=lt(As),[e,n]=lt(oa),[r]=lt(tp),[i]=lt(HE),[,s]=lt(np),[,o]=lt(zE),[l]=lt(ws);T.useEffect(()=>{r?.length&&r.length===1&&c(r[0])},[]);const c=f=>{n(f),i.length<2&&d(i?.[0],f)},d=(f,p)=>{n(e||p||null),s(f),o({...ew,agent_id:e?.id,customer_id:f.id}),t(ew),l.closeDialog()};return w.jsxs("div",{className:"h-full flex flex-col",children:[w.jsx(PE,{children:w.jsx(FE,{children:w.jsxs("div",{className:"mb-[12px] mt-[24px] w-full flex justify-between items-center ps-[30px] pe-[20px]",children:[w.jsx(BE,{className:"text-[20px] font-semibold",children:e?"Select a Customer":"Select an Agent"}),w.jsx("img",{role:"button",tabIndex:0,onKeyDown:vs,onClick:l.closeDialog,className:"cursor-pointer rounded-full",src:"icons/close.svg",alt:"close",height:24,width:24})]})})}),w.jsx("div",{className:"flex flex-col fixed-scroll overflow-auto relative flex-1",children:(e?i:r)?.map(f=>w.jsxs("div",{"data-testid":"agent",tabIndex:0,onKeyDown:vs,role:"button",onClick:()=>e?d(f):c(f),className:Al("cursor-pointer hover:bg-[#FBFBFB] min-h-[78px] h-[78px] w-full border-b-[0.6px] border-b-solid border-b-[#EBECF0] flex items-center ps-[30px] pe-[20px]"),children:[w.jsx(H0,{agent:f,tooltip:!1}),w.jsxs("div",{children:[w.jsx("div",{className:"text-[16px] font-medium",children:f.id==="guest"?"Guest":f.name}),w.jsxs("div",{className:"text-[14px] font-light text-[#A9A9A9]",children:["(id=",f.id,")"]})]})]},f.id))})]})},AF=xA,kF=vA,NF=wA,IA=T.forwardRef(({className:t,...e},n)=>w.jsx(Qh,{className:Rt("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",t),...e,ref:n}));IA.displayName=Qh.displayName;const RF=V_("fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",{variants:{side:{top:"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",bottom:"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",left:"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",right:"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm"}},defaultVariants:{side:"right"}}),OA=T.forwardRef(({side:t="right",className:e,children:n,...r},i)=>w.jsxs(NF,{children:[w.jsx(IA,{}),w.jsxs(Zh,{ref:i,className:Rt(RF({side:t}),e),...r,children:[n,w.jsxs(ME,{className:"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary",children:[w.jsx(gl,{className:"h-4 w-4"}),w.jsx("span",{className:"sr-only",children:"Close"})]})]})]}));OA.displayName=Zh.displayName;const MA=({className:t,...e})=>w.jsx("div",{className:Rt("flex flex-col space-y-2 text-center sm:text-left",t),...e});MA.displayName="SheetHeader";const DA=T.forwardRef(({className:t,...e},n)=>w.jsx(Jh,{ref:n,className:Rt("text-lg font-semibold text-foreground",t),...e}));DA.displayName=Jh.displayName;const LA=T.forwardRef(({className:t,...e},n)=>w.jsx(ep,{ref:n,className:Rt("text-sm text-muted-foreground",t),...e}));LA.displayName=ep.displayName;const PA=({children:t,className:e})=>w.jsxs("div",{className:Xe("h-[70px] bg-white min-h-[70px] rounded-se-[16px] border-[#F3F5F9] rounded-ss-[16px] flex justify-between sticky top-0 z-10",e),children:[w.jsx("div",{className:"w-[12px] min-w-[12px]"}),t,w.jsx("div",{className:"w-[12px] min-w-[12px]"})]}),Hi="NEW_SESSION",FA=({setFilterSessionVal:t,filterSessionVal:e})=>{const[n,r]=T.useState(!1),[i,s]=lt(As),[,o]=lt(oa),[l]=lt(ws);T.useEffect(()=>{n&&r(!1)},[i]);const c=()=>{s(null),o(null),l.openDialog("",w.jsx(RA,{}),{height:"536px",width:"604px"})};return w.jsx(PA,{className:"z-60 overflow-visible rounded-s-[16px] ",children:w.jsxs("div",{className:"w-[352px] rounded-ss-[16px] rounded-se-[16px] boder-b-[0.6px] border-b-[#ebecf0] max-mobile:w-full h-[70px] flex items-center max-mobile:justify-between bg-white",children:[w.jsxs("div",{className:"flex items-center min-[801px]:hidden",children:[w.jsx("div",{className:"flex items-center",children:w.jsx("img",{src:"/chat/app-logo.svg",alt:"logo","aria-hidden":!0,className:"self-center h-[30px]"})}),w.jsx("div",{children:w.jsxs(AF,{open:n,onOpenChange:()=>r(!n),children:[w.jsx(kF,{asChild:!0,onClick:()=>r(!0),children:w.jsx(P4,{className:"ms-[24px] cursor-pointer"})}),w.jsxs(OA,{side:"left",className:"w-fit p-0 [&>button[type=button]]:hidden",children:[w.jsxs(MA,{children:[w.jsx(DA,{className:"text-center"}),w.jsx(LA,{})]}),w.jsxs("div",{className:"flex items-center px-[12px] flex-1 relative !shadow-main",children:[w.jsx("img",{src:"icons/search.svg",alt:"",className:"absolute left-[24px]"}),w.jsx(sc,{placeholder:"Filter sessions",onChange:d=>t(d.target.value),className:"!ring-0 !ring-offset-0 h-[38px] w-full placeholder:font-light ps-[35px] rounded-[6px] !pointer-events-auto"})]}),w.jsx(UA,{filterSessionVal:e})]})]})})]}),w.jsx("a",{href:"https://parlant.io",target:"_blank",className:"flex items-center ms-[4px] -me-[6px] max-mobile:hidden",children:w.jsx("img",{src:"/chat/app-logo.svg",alt:"logo","aria-hidden":!0,className:"self-center h-[30px]"})}),w.jsxs("div",{className:"flex items-center ps-[12px] flex-1 relative !shadow-main max-mobile:hidden",children:[w.jsx("img",{src:"icons/search.svg",alt:"",className:"absolute left-[24px]"}),w.jsx(sc,{placeholder:"Filter sessions",onChange:d=>t(d.target.value),className:"!ring-0 !ring-offset-0 h-[38px] w-full placeholder:font-light ps-[35px] rounded-[6px] !pointer-events-auto"})]}),w.jsx("div",{className:"group ms-[8px]",children:w.jsx(Xn,{value:"New Session",side:"right",className:"group",children:w.jsxs(w.Fragment,{children:[w.jsx("img",{src:"buttons/new-session.svg",alt:"add session",className:"shadow-main cursor-pointer group-hover:hidden",tabIndex:1,role:"button",onKeyDown:vs,onClick:c}),w.jsx("img",{src:"buttons/new-session-hover.svg",alt:"add session",className:"shadow-main cursor-pointer hidden group-hover:block",tabIndex:1,role:"button",onKeyDown:vs,onClick:c})]})})})]})})};function BA({text:t,textToCopy:e,preText:n,className:r,element:i}){e||(e=t);const s=o=>{o.stopPropagation(),navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(e).then(()=>Ir.info(`Copied text: ${e}`)).catch(()=>{Xf(e,i)}):Xf(e,i)};return w.jsxs("div",{className:Xe("group flex gap-[6px] items-center cursor-pointer text-[#A9A9A9] text-[15px] font-light",r),onKeyDown:vs,onClick:s,children:[w.jsxs("div",{className:"flex items-center gap-[6px]",children:[n&&w.jsx("span",{className:"font-semibold",children:n}),w.jsx("span",{className:"group-hover:text-[#656565]",children:t})]}),w.jsx("div",{className:"copy-icon hidden group-hover:block group-hover:text-[#656565]",role:"button",tabIndex:0,children:w.jsx("img",{src:"icons/copy.svg",alt:""})})]})}const IF=({session:t,closeDialog:e,deleteClicked:n})=>w.jsxs("div",{"data-testid":"deleteDialogContent",children:[w.jsx(z0,{session:t,disabled:!0,className:"[&_.title]:max-w-[90%]"}),w.jsxs("div",{className:"h-[80px] flex items-center justify-end pe-[18px]",children:[w.jsx(An,{"data-testid":"cancel-delete",onClick:e,className:"h-[46px] w-[96px] !bg-white text-[#656565] hover:text-[#151515] rounded-[6px] py-[12px] px-[24px] me-[10px] text-[16px] font-normal border",children:"Cancel"}),w.jsx(An,{"data-testid":"gradient-button",onClick:n,className:"h-[46px] w-[161px] bg-green-main hover:bg-green-hover rounded-[6px] py-[10px] px-[29.5px] text-[15px] font-medium",children:"Delete Session"})]})]});function z0({session:t,isSelected:e,refetch:n,editingTitle:r,setEditingTitle:i,tabIndex:s,disabled:o,className:l}){const c=T.useRef(null),[d]=lt(tp),[f]=lt(HE),[p,m]=T.useState(new Map),[g,x]=T.useState(new Map),[,v]=lt(As),[,S]=lt(oa),[,C]=lt(np),[,A]=lt(zE),[,k]=lt(rp),[M]=lt(ws),[F,I]=T.useState(!1),D=T.useRef(null);T.useEffect(()=>{e&&(t.id===Hi&&!t.agent_id?S(null):(S(d?.find(j=>j.id===t.agent_id)||null),C(f?.find(j=>j.id===t.customer_id)||null)))},[e,S,t.id,t.agent_id,t.title]),T.useEffect(()=>{d&&m(new Map(d.map(j=>[j.id,j])))},[d]),T.useEffect(()=>{f&&x(new Map(f.map(j=>[j.id,j])))},[f]);const G=async j=>{j.stopPropagation();const W=O=>{if(M.closeDialog(),O.stopPropagation(),t.id===Hi){A(null),v(null),S(null);return}return I(!0),e&&(v(null),document.title="Parlant"),JS(`sessions/${t.id}`).then(()=>{k(U=>U.filter(Q=>Q.id!==t.id)),Ir.success(`Session "${t.title}" deleted successfully`),I(!1)}).catch(()=>{Ir.error("Something went wrong"),I(!1)})};M.openDialog("Delete Session",w.jsx(IF,{closeDialog:M.closeDialog,deleteClicked:W,session:t}),{height:"230px",width:"480px"},()=>document.body.style.pointerEvents="auto")},X=async j=>{const W=await a_("Parlant-flags","message_flags","sessionIndex",t.id,{name:"sessionIndex",keyPath:"sessionId"},!0);j.stopPropagation();try{const U=(await P(t.id)||[]).filter(ue=>ue.kind==="message"),Q=[];U?.length&&U.forEach(ue=>{Q.push({"Trace ID":ue.trace_id,Source:ue.source==="ai_agent"?"AI Agent":"Customer",Participant:ue?.data?.participant?.display_name||"",Timestamp:ue.creation_utc||"",Message:ue.data?.message||"",Draft:ue.data?.draft||"",Tags:ue.data?.tags||"",Flag:W?.[ue.trace_id]||""})});const R=["Trace ID","Source","Participant","Timestamp","Message","Draft","Tags","Flag"],oe=`session_${t.id}_"${t.title.replace(/[^a-zA-Z0-9]/g,"_")}.csv`;if(qD(Q,oe,{headers:R,dateFormat:"readable"}))Ir.success(`Session "${t.title}" exported successfully`);else throw new Error("Export failed")}catch(O){console.error("Export failed:",O),Ir.error("Failed to export session")}},P=async j=>{try{const W=await fetch(`${lo}/sessions/${j}/events`);if(!W.ok)throw new Error("Failed to fetch session data");return await W.json()}catch(W){return console.error("Failed to fetch session data:",W),{messages:[]}}},Y=async j=>{j.stopPropagation(),i?.(t.id),setTimeout(()=>c?.current?.select(),0)},z=j=>{j.stopPropagation();const W=c?.current?.value?.trim();if(W){if(t.id===Hi){i?.(null),A(O=>O&&{...O,title:W}),Ir.success("title changed successfully");return}WM(`sessions/${t.id}`,{title:W}).then(()=>{i?.(null),n?.(),Ir.success("title changed successfully")}).catch(()=>{Ir.error("Something went wrong")})}},ie=j=>{j.stopPropagation(),i?.(null)},Z=j=>{j.key==="Enter"&&z(j)},ee=[{title:"copy ID",onClick:j=>{j.stopPropagation(),hl(t.id,D?.current||void 0)},imgPath:"icons/copy-session.svg"},{title:"rename",onClick:Y,imgPath:"icons/rename.svg"},{title:"export",onClick:X,imgPath:"icons/export.svg"},{title:"delete",onClick:G,imgPath:"icons/delete.svg"}],ae=p.get(t.agent_id),de=g.get(t.customer_id);return w.jsx(Xn,{value:w.jsx("div",{className:"font-light text-[#a9a9a9] flex items-center",children:w.jsx(BA,{preText:"Session ID:",textToCopy:t.id,text:t.id,className:"!text-[#a9a9a9] hover:text-[#151515] !text-[13px] ms-[4px] [&_img]:opacity-60 [&_.copy-icon]:!block"})}),side:"right",children:w.jsxs("div",{"data-testid":"session",role:"button",tabIndex:s,onKeyDown:vs,onClick:()=>!o&&!r&&!F&&v(t),className:Xe("bg-white animate-fade-in text-[14px] hover:rounded-[6px] font-inter justify-between font-medium border-b-[0.6px] border-b-solid border-[#F9FAFC] cursor-pointer p-1 flex items-center ps-[8px] min-h-[74px] h-[74px] ml-0 mr-0 ",e&&" rounded-[6px]",r===t.id?K4.editSession+" !p-[4px_2px] ":r?" opacity-[33%] ":" hover:bg-main ",e&&r!==t.id?"!bg-[#F5F6F8]":"",o?" pointer-events-none":"",F?"opacity-[33%]":"",l),children:[w.jsxs("div",{className:"title flex-1 whitespace-nowrap flex overflow-hidden max-w-[210px] ms-[4px] h-[48px]",children:[r!==t.id&&w.jsxs("div",{className:"overflow-visible overflow-ellipsis flex items-center",children:[w.jsx("div",{children:w.jsx(H0,{agent:ae||{id:"",name:"N/A"},customer:de||{id:"",name:"N/A"}})}),w.jsxs("div",{className:On(!ae&&"opacity-50","ms-[4px] text-[15px]"),children:[t.title,w.jsxs("small",{className:"text-[13px] text-[#A9A9A9] -mb-[7px] font-light flex gap-[6px]",children:[sA(t.creation_utc),w.jsx("img",{src:"icons/dot-saparetor.svg",alt:"",height:18,width:3}),V4(t.creation_utc)]})]})]}),r===t.id&&w.jsxs("div",{className:"flex items-center ps-[6px]",children:[w.jsx("div",{children:ae&&w.jsx(H0,{agent:ae})}),w.jsx(sc,{"data-testid":"sessionTitle",ref:c,onKeyUp:Z,onClick:j=>j.stopPropagation(),defaultValue:t.title,className:"box-shadow-none border-none bg-[#F5F6F8] text-foreground h-fit p-1 ms-[6px]"})]})]}),w.jsxs("div",{className:"h-[39px] flex items-center",children:[!o&&r!==t.id&&t.id!==Hi&&w.jsxs(rA,{children:[w.jsx(iA,{disabled:!!r,className:"outline-none","data-testid":"menu-button",tabIndex:-1,onClick:j=>j.stopPropagation(),children:w.jsx("div",{tabIndex:s,role:"button",className:"rounded-full me-[14px]",onClick:j=>j.stopPropagation(),children:w.jsx("img",{src:"icons/more.svg",alt:"more",height:14,width:14})})}),w.jsx(AE,{ref:D,side:"right",align:"start",className:"-ms-[10px] flex flex-col gap-[8px] py-[14px] px-[10px] border-none w-[168px] [box-shadow:_0px_8px_20px_-8px_#00000012] rounded-[8px]",children:ee.map(j=>w.jsxs(rh,{tabIndex:0,onClick:j.onClick,className:"gap-0 font-normal text-[14px] px-[20px] font-inter capitalize hover:!bg-[#FAF9FF]",children:[w.jsx("img",{"data-testid":j.title,src:j.imgPath,height:16,width:18,className:"me-[8px]",alt:""}),j.title]},j.title))})]}),r==t.id&&w.jsxs("div",{className:"me-[18px]",children:[w.jsx(Xn,{value:"Cancel",children:w.jsx(An,{"data-testid":"cancel",variant:"ghost",className:"w-[28px] h-[28px] p-[8px] rounded-full",onClick:ie,children:w.jsx("img",{src:"icons/cancel.svg",alt:"cancel"})})}),w.jsx(Xn,{value:"Save",children:w.jsx(An,{variant:"ghost",className:"w-[28px] h-[28px] p-[8px] rounded-full",onClick:z,children:w.jsx("img",{src:"icons/save.svg",alt:"cancel"})})})]})]})]},t.id)})}function UA({filterSessionVal:t}){const[e,n]=T.useState(null),[r]=lt(As),{data:i,ErrorTemplate:s,loading:o,refetch:l}=hg("sessions"),{data:c}=hg("agents"),{data:d}=hg("customers"),[,f]=lt(tp),[,p]=lt(HE),[m]=lt(oa),[g]=lt(np),[x,v]=lt(rp),[S,C]=T.useState(x);return T.useEffect(()=>{c&&f(c)},[c]),T.useEffect(()=>{d&&p(d)},[d]),T.useEffect(()=>{i&&v(i)},[i]),T.useEffect(()=>{t?.trim()?C(x.filter(A=>A.title?.toLowerCase()?.includes(t?.toLowerCase())||A.id?.toLowerCase()?.includes(t?.toLowerCase()))):C(x)},[t,x]),w.jsx("div",{className:On("flex flex-col items-center h-[calc(100%-68px)] border-e"),children:w.jsxs("div",{"data-testid":"sessions",className:"bg-white px-[12px] border-b-[12px] border-white flex-1 fixed-scroll justify-center w-[352px] overflow-auto rounded-es-[16px] rounded-ee-[16px]",children:[o&&!x?.length&&w.jsx("div",{children:"loading..."}),r?.id===ah&&w.jsx(z0,{className:"opacity-50","data-testid":"session",isSelected:!0,session:{...r,agent_id:m?.id||"",customer_id:g?.id||""}},ah),S.toReversed().map((A,k)=>w.jsx(z0,{"data-testid":"session",tabIndex:x.length-k,editingTitle:e,setEditingTitle:n,isSelected:A.id===r?.id,refetch:l,session:A},A.id)),s&&w.jsx(s,{})]})})}class HA extends T.Component{constructor(e){super(e),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e){this.setState({errorStack:e.stack})}render(){return this.state.hasError?this.props.component||w.jsxs("div",{className:"flex bg-main items-center justify-center h-screen flex-col",children:[w.jsx("img",{src:"/chat/logo-color.svg",alt:"Logo",height:200,width:200,className:"mb-[10px]"}),w.jsx("h1",{className:"text-[20px]",children:"Oops! Something went wrong"}),w.jsxs("p",{className:"text-center",children:["We apologize for the inconvenience. Please try again later, or"," ",w.jsx("a",{href:"/",className:"underline",children:"try again now"}),"."]}),w.jsx("div",{className:"flex justify-center max-h-[300px] mt-[40px] bg-[#f0eeee] rounded-[10px] p-[10px] break-words border border-solid border-[#dedcdc]",children:w.jsx("code",{className:"max-h-[300px] w-[600px] max-w-[80vw] overflow-auto",children:this.state.errorStack})})]}):this.props.children}}const zA=()=>{const[t,e]=T.useState(null),[n,r]=T.useState(null),[i,s]=T.useState({height:"",width:""}),[o,l]=T.useState(null),c=(p,m,g,x=null)=>{p&&e(p),r(m),s({height:g.height,width:g.width}),x&&l(x)},d=p=>{p?.stopPropagation(),r(null),e(null),o?.(),l(null)};return{openDialog:c,DialogComponent:()=>w.jsx(TA,{open:!!n,children:w.jsx(DE,{children:w.jsx(LE,{"data-testid":"dialog","aria-hidden":!1,style:{maxHeight:i.height,width:i.width},className:"[&>button]:hidden z-[99] !pointer-events-auto p-0 h-[80%] font-inter bg-white block max-w-[95%]",children:w.jsxs("div",{className:"bg-white h-full rounded-[12px] flex flex-col","aria-hidden":!1,children:[w.jsx(PE,{className:Al(!t&&"hidden"),children:w.jsx(RE,{children:w.jsxs("div",{className:"mb-[12px] mt-[24px] w-full flex justify-between items-center ps-[30px] pe-[20px]",children:[w.jsx(IE,{className:"text-[20px] font-semibold",children:t}),w.jsx("img",{role:"button",tabIndex:0,onKeyDown:vs,onClick:d,className:"cursor-pointer rounded-full",src:"icons/close.svg",alt:"close",width:24,height:24})]})})}),w.jsx("div",{className:"overflow-auto flex-1",children:n})]})})})}),closeDialog:d}};var Ng={exports:{}},Rg,tw;function OF(){if(tw)return Rg;tw=1;var t="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";return Rg=t,Rg}var Ig,nw;function MF(){if(nw)return Ig;nw=1;var t=OF();function e(){}function n(){}return n.resetWarningCache=e,Ig=function(){function r(o,l,c,d,f,p){if(p!==t){var m=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw m.name="Invariant Violation",m}}r.isRequired=r;function i(){return r}var s={array:r,bigint:r,bool:r,func:r,number:r,object:r,string:r,symbol:r,any:r,arrayOf:i,element:r,elementType:r,instanceOf:i,node:r,objectOf:i,oneOf:i,oneOfType:i,shape:i,exact:i,checkPropTypes:n,resetWarningCache:e};return s.PropTypes=s,s},Ig}var rw;function DF(){return rw||(rw=1,Ng.exports=MF()()),Ng.exports}var LF=DF();const un=_s(LF);var Og,iw;function PF(){if(iw)return Og;iw=1;function t(l){return l&&typeof l=="object"&&"default"in l?l.default:l}var e=Dh(),n=t(e);function r(l,c,d){return c in l?Object.defineProperty(l,c,{value:d,enumerable:!0,configurable:!0,writable:!0}):l[c]=d,l}function i(l,c){l.prototype=Object.create(c.prototype),l.prototype.constructor=l,l.__proto__=c}var s=!!(typeof window<"u"&&window.document&&window.document.createElement);function o(l,c,d){if(typeof l!="function")throw new Error("Expected reducePropsToState to be a function.");if(typeof c!="function")throw new Error("Expected handleStateChangeOnClient to be a function.");if(typeof d<"u"&&typeof d!="function")throw new Error("Expected mapStateOnServer to either be undefined or a function.");function f(p){return p.displayName||p.name||"Component"}return function(m){if(typeof m!="function")throw new Error("Expected WrappedComponent to be a React component.");var g=[],x;function v(){x=l(g.map(function(C){return C.props})),S.canUseDOM?c(x):d&&(x=d(x))}var S=(function(C){i(A,C);function A(){return C.apply(this,arguments)||this}A.peek=function(){return x},A.rewind=function(){if(A.canUseDOM)throw new Error("You may only call rewind() on the server. Call peek() to read the current state.");var F=x;return x=void 0,g=[],F};var k=A.prototype;return k.UNSAFE_componentWillMount=function(){g.push(this),v()},k.componentDidUpdate=function(){v()},k.componentWillUnmount=function(){var F=g.indexOf(this);g.splice(F,1),v()},k.render=function(){return n.createElement(m,this.props)},A})(e.PureComponent);return r(S,"displayName","SideEffect("+f(m)+")"),r(S,"canUseDOM",s),S}}return Og=o,Og}var FF=PF();const BF=_s(FF);var Mg,sw;function UF(){if(sw)return Mg;sw=1;var t=typeof Element<"u",e=typeof Map=="function",n=typeof Set=="function",r=typeof ArrayBuffer=="function"&&!!ArrayBuffer.isView;function i(s,o){if(s===o)return!0;if(s&&o&&typeof s=="object"&&typeof o=="object"){if(s.constructor!==o.constructor)return!1;var l,c,d;if(Array.isArray(s)){if(l=s.length,l!=o.length)return!1;for(c=l;c--!==0;)if(!i(s[c],o[c]))return!1;return!0}var f;if(e&&s instanceof Map&&o instanceof Map){if(s.size!==o.size)return!1;for(f=s.entries();!(c=f.next()).done;)if(!o.has(c.value[0]))return!1;for(f=s.entries();!(c=f.next()).done;)if(!i(c.value[1],o.get(c.value[0])))return!1;return!0}if(n&&s instanceof Set&&o instanceof Set){if(s.size!==o.size)return!1;for(f=s.entries();!(c=f.next()).done;)if(!o.has(c.value[0]))return!1;return!0}if(r&&ArrayBuffer.isView(s)&&ArrayBuffer.isView(o)){if(l=s.length,l!=o.length)return!1;for(c=l;c--!==0;)if(s[c]!==o[c])return!1;return!0}if(s.constructor===RegExp)return s.source===o.source&&s.flags===o.flags;if(s.valueOf!==Object.prototype.valueOf&&typeof s.valueOf=="function"&&typeof o.valueOf=="function")return s.valueOf()===o.valueOf();if(s.toString!==Object.prototype.toString&&typeof s.toString=="function"&&typeof o.toString=="function")return s.toString()===o.toString();if(d=Object.keys(s),l=d.length,l!==Object.keys(o).length)return!1;for(c=l;c--!==0;)if(!Object.prototype.hasOwnProperty.call(o,d[c]))return!1;if(t&&s instanceof Element)return!1;for(c=l;c--!==0;)if(!((d[c]==="_owner"||d[c]==="__v"||d[c]==="__o")&&s.$$typeof)&&!i(s[d[c]],o[d[c]]))return!1;return!0}return s!==s&&o!==o}return Mg=function(o,l){try{return i(o,l)}catch(c){if((c.message||"").match(/stack|recursion/i))return console.warn("react-fast-compare cannot handle circular refs"),!1;throw c}},Mg}var HF=UF();const zF=_s(HF);/* object-assign (c) Sindre Sorhus @license MIT */var Dg,ow;function jF(){if(ow)return Dg;ow=1;var t=Object.getOwnPropertySymbols,e=Object.prototype.hasOwnProperty,n=Object.prototype.propertyIsEnumerable;function r(s){if(s==null)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(s)}function i(){try{if(!Object.assign)return!1;var s=new String("abc");if(s[5]="de",Object.getOwnPropertyNames(s)[0]==="5")return!1;for(var o={},l=0;l<10;l++)o["_"+String.fromCharCode(l)]=l;var c=Object.getOwnPropertyNames(o).map(function(f){return o[f]});if(c.join("")!=="0123456789")return!1;var d={};return"abcdefghijklmnopqrst".split("").forEach(function(f){d[f]=f}),Object.keys(Object.assign({},d)).join("")==="abcdefghijklmnopqrst"}catch{return!1}}return Dg=i()?Object.assign:function(s,o){for(var l,c=r(s),d,f=1;f=0||Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n},QF=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e&&(typeof e=="object"||typeof e=="function")?e:t},j0=function(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return n===!1?String(e):String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},ZF=function(e){var n=sl(e,ct.TITLE),r=sl(e,dc.TITLE_TEMPLATE);if(r&&n)return r.replace(/%s/g,function(){return Array.isArray(n)?n.join(""):n});var i=sl(e,dc.DEFAULT_TITLE);return n||i||void 0},JF=function(e){return sl(e,dc.ON_CHANGE_CLIENT_STATE)||function(){}},Lg=function(e,n){return n.filter(function(r){return typeof r[e]<"u"}).map(function(r){return r[e]}).reduce(function(r,i){return kr({},r,i)},{})},eB=function(e,n){return n.filter(function(r){return typeof r[ct.BASE]<"u"}).map(function(r){return r[ct.BASE]}).reverse().reduce(function(r,i){if(!r.length)for(var s=Object.keys(i),o=0;o=0;r--){var i=e[r];if(i.hasOwnProperty(n))return i[n]}return null},tB=function(e){return{baseTag:eB([dn.HREF,dn.TARGET],e),bodyAttributes:Lg($o.BODY,e),defer:sl(e,dc.DEFER),encode:sl(e,dc.ENCODE_SPECIAL_CHARACTERS),htmlAttributes:Lg($o.HTML,e),linkTags:Cu(ct.LINK,[dn.REL,dn.HREF],e),metaTags:Cu(ct.META,[dn.NAME,dn.CHARSET,dn.HTTPEQUIV,dn.PROPERTY,dn.ITEM_PROP],e),noscriptTags:Cu(ct.NOSCRIPT,[dn.INNER_HTML],e),onChangeClientState:JF(e),scriptTags:Cu(ct.SCRIPT,[dn.SRC,dn.INNER_HTML],e),styleTags:Cu(ct.STYLE,[dn.CSS_TEXT],e),title:ZF(e),titleAttributes:Lg($o.TITLE,e)}},$0=(function(){var t=Date.now();return function(e){var n=Date.now();n-t>16?(t=n,e(n)):setTimeout(function(){$0(e)},0)}})(),lw=function(e){return clearTimeout(e)},nB=typeof window<"u"?window.requestAnimationFrame&&window.requestAnimationFrame.bind(window)||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||$0:global.requestAnimationFrame||$0,rB=typeof window<"u"?window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||lw:global.cancelAnimationFrame||lw,iB=function(e){return console&&typeof console.warn=="function"&&console.warn(e)},Au=null,sB=function(e){Au&&rB(Au),e.defer?Au=nB(function(){uw(e,function(){Au=null})}):(uw(e),Au=null)},uw=function(e,n){var r=e.baseTag,i=e.bodyAttributes,s=e.htmlAttributes,o=e.linkTags,l=e.metaTags,c=e.noscriptTags,d=e.onChangeClientState,f=e.scriptTags,p=e.styleTags,m=e.title,g=e.titleAttributes;W0(ct.BODY,i),W0(ct.HTML,s),oB(m,g);var x={baseTag:Ha(ct.BASE,r),linkTags:Ha(ct.LINK,o),metaTags:Ha(ct.META,l),noscriptTags:Ha(ct.NOSCRIPT,c),scriptTags:Ha(ct.SCRIPT,f),styleTags:Ha(ct.STYLE,p)},v={},S={};Object.keys(x).forEach(function(C){var A=x[C],k=A.newTags,M=A.oldTags;k.length&&(v[C]=k),M.length&&(S[C]=x[C].oldTags)}),n&&n(),d(e,v,S)},jA=function(e){return Array.isArray(e)?e.join(""):e},oB=function(e,n){typeof e<"u"&&document.title!==e&&(document.title=jA(e)),W0(ct.TITLE,n)},W0=function(e,n){var r=document.getElementsByTagName(e)[0];if(r){for(var i=r.getAttribute(Ei),s=i?i.split(","):[],o=[].concat(s),l=Object.keys(n),c=0;c=0;m--)r.removeAttribute(o[m]);s.length===o.length?r.removeAttribute(Ei):r.getAttribute(Ei)!==l.join(",")&&r.setAttribute(Ei,l.join(","))}},Ha=function(e,n){var r=document.head||document.querySelector(ct.HEAD),i=r.querySelectorAll(e+"["+Ei+"]"),s=Array.prototype.slice.call(i),o=[],l=void 0;return n&&n.length&&n.forEach(function(c){var d=document.createElement(e);for(var f in c)if(c.hasOwnProperty(f))if(f===dn.INNER_HTML)d.innerHTML=c.innerHTML;else if(f===dn.CSS_TEXT)d.styleSheet?d.styleSheet.cssText=c.cssText:d.appendChild(document.createTextNode(c.cssText));else{var p=typeof c[f]>"u"?"":c[f];d.setAttribute(f,p)}d.setAttribute(Ei,"true"),s.some(function(m,g){return l=g,d.isEqualNode(m)})?s.splice(l,1):o.push(d)}),s.forEach(function(c){return c.parentNode.removeChild(c)}),o.forEach(function(c){return r.appendChild(c)}),{oldTags:s,newTags:o}},$A=function(e){return Object.keys(e).reduce(function(n,r){var i=typeof e[r]<"u"?r+'="'+e[r]+'"':""+r;return n?n+" "+i:i},"")},aB=function(e,n,r,i){var s=$A(r),o=jA(n);return s?"<"+e+" "+Ei+'="true" '+s+">"+j0(o,i)+"":"<"+e+" "+Ei+'="true">'+j0(o,i)+""},lB=function(e,n,r){return n.reduce(function(i,s){var o=Object.keys(s).filter(function(d){return!(d===dn.INNER_HTML||d===dn.CSS_TEXT)}).reduce(function(d,f){var p=typeof s[f]>"u"?f:f+'="'+j0(s[f],r)+'"';return d?d+" "+p:p},""),l=s.innerHTML||s.cssText||"",c=GF.indexOf(e)===-1;return i+"<"+e+" "+Ei+'="true" '+o+(c?"/>":">"+l+"")},"")},WA=function(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return Object.keys(e).reduce(function(r,i){return r[lh[i]||i]=e[i],r},n)},uB=function(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return Object.keys(e).reduce(function(r,i){return r[VF[i]||i]=e[i],r},n)},cB=function(e,n,r){var i,s=(i={key:n},i[Ei]=!0,i),o=WA(r,s);return[we.createElement(ct.TITLE,o,n)]},dB=function(e,n){return n.map(function(r,i){var s,o=(s={key:i},s[Ei]=!0,s);return Object.keys(r).forEach(function(l){var c=lh[l]||l;if(c===dn.INNER_HTML||c===dn.CSS_TEXT){var d=r.innerHTML||r.cssText;o.dangerouslySetInnerHTML={__html:d}}else o[c]=r[l]}),we.createElement(e,o)})},ds=function(e,n,r){switch(e){case ct.TITLE:return{toComponent:function(){return cB(e,n.title,n.titleAttributes)},toString:function(){return aB(e,n.title,n.titleAttributes,r)}};case $o.BODY:case $o.HTML:return{toComponent:function(){return WA(n)},toString:function(){return $A(n)}};default:return{toComponent:function(){return dB(e,n)},toString:function(){return lB(e,n,r)}}}},VA=function(e){var n=e.baseTag,r=e.bodyAttributes,i=e.encode,s=e.htmlAttributes,o=e.linkTags,l=e.metaTags,c=e.noscriptTags,d=e.scriptTags,f=e.styleTags,p=e.title,m=p===void 0?"":p,g=e.titleAttributes;return{base:ds(ct.BASE,n,i),bodyAttributes:ds($o.BODY,r,i),htmlAttributes:ds($o.HTML,s,i),link:ds(ct.LINK,o,i),meta:ds(ct.META,l,i),noscript:ds(ct.NOSCRIPT,c,i),script:ds(ct.SCRIPT,d,i),style:ds(ct.STYLE,f,i),title:ds(ct.TITLE,{title:m,titleAttributes:g},i)}},fB=function(e){var n,r;return r=n=(function(i){XF(s,i);function s(){return YF(this,s),QF(this,i.apply(this,arguments))}return s.prototype.shouldComponentUpdate=function(l){return!zF(this.props,l)},s.prototype.mapNestedChildrenToProps=function(l,c){if(!c)return null;switch(l.type){case ct.SCRIPT:case ct.NOSCRIPT:return{innerHTML:c};case ct.STYLE:return{cssText:c}}throw new Error("<"+l.type+" /> elements are self-closing and can not contain children. Refer to our API for more information.")},s.prototype.flattenArrayTypeChildren=function(l){var c,d=l.child,f=l.arrayTypeChildren,p=l.newChildProps,m=l.nestedChildren;return kr({},f,(c={},c[d.type]=[].concat(f[d.type]||[],[kr({},p,this.mapNestedChildrenToProps(d,m))]),c))},s.prototype.mapObjectTypeChildren=function(l){var c,d,f=l.child,p=l.newProps,m=l.newChildProps,g=l.nestedChildren;switch(f.type){case ct.TITLE:return kr({},p,(c={},c[f.type]=g,c.titleAttributes=kr({},m),c));case ct.BODY:return kr({},p,{bodyAttributes:kr({},m)});case ct.HTML:return kr({},p,{htmlAttributes:kr({},m)})}return kr({},p,(d={},d[f.type]=kr({},m),d))},s.prototype.mapArrayTypeChildrenToProps=function(l,c){var d=kr({},c);return Object.keys(l).forEach(function(f){var p;d=kr({},d,(p={},p[f]=l[f],p))}),d},s.prototype.warnOnInvalidChildren=function(l,c){return!0},s.prototype.mapChildrenToProps=function(l,c){var d=this,f={};return we.Children.forEach(l,function(p){if(!(!p||!p.props)){var m=p.props,g=m.children,x=aw(m,["children"]),v=uB(x);switch(d.warnOnInvalidChildren(p,g),p.type){case ct.LINK:case ct.META:case ct.NOSCRIPT:case ct.SCRIPT:case ct.STYLE:f=d.flattenArrayTypeChildren({child:p,arrayTypeChildren:f,newChildProps:v,nestedChildren:g});break;default:c=d.mapObjectTypeChildren({child:p,newProps:c,newChildProps:v,nestedChildren:g});break}}}),c=this.mapArrayTypeChildrenToProps(f,c),c},s.prototype.render=function(){var l=this.props,c=l.children,d=aw(l,["children"]),f=kr({},d);return c&&(f=this.mapChildrenToProps(c,f)),we.createElement(e,f)},qF(s,null,[{key:"canUseDOM",set:function(l){e.canUseDOM=l}}]),s})(we.Component),n.propTypes={base:un.object,bodyAttributes:un.object,children:un.oneOfType([un.arrayOf(un.node),un.node]),defaultTitle:un.string,defer:un.bool,encodeSpecialCharacters:un.bool,htmlAttributes:un.object,link:un.arrayOf(un.object),meta:un.arrayOf(un.object),noscript:un.arrayOf(un.object),onChangeClientState:un.func,script:un.arrayOf(un.object),style:un.arrayOf(un.object),title:un.string,titleAttributes:un.object,titleTemplate:un.string},n.defaultProps={defer:!0,encodeSpecialCharacters:!0},n.peek=e.peek,n.rewind=function(){var i=e.rewind();return i||(i=VA({baseTag:[],bodyAttributes:{},htmlAttributes:{},linkTags:[],metaTags:[],noscriptTags:[],scriptTags:[],styleTags:[],title:"",titleAttributes:{}})),i},r},hB=function(){return null},pB=BF(tB,sB,VA)(hB),V0=fB(pB);V0.renderStatic=V0.rewind;const ip=T.forwardRef(({className:t,...e},n)=>w.jsx("textarea",{className:Rt("flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",t),ref:n,...e}));ip.displayName="Textarea";function mB(t,e){return t.reduce((n,r)=>{let i=e(r);return i||(i=i?.toString()),n[i]||(n[i]=[]),n[i].push(r),n},{})}const gB=()=>w.jsx("div",{className:"w-[16px] min-w-[16px]"}),Qa=T.memo(gB);function cw(t){const e=[],n=String(t||"");let r=n.indexOf(","),i=0,s=!1;for(;!s;){r===-1&&(r=n.length,s=!0);const o=n.slice(i,r).trim();(o||!s)&&e.push(o),i=r+1,r=n.indexOf(",",i)}return e}function GA(t,e){const n={};return(t[t.length-1]===""?[...t,""]:t).join((n.padRight?" ":"")+","+(n.padLeft===!1?"":" ")).trim()}const bB=/^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,EB=/^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,yB={};function dw(t,e){return(yB.jsx?EB:bB).test(t)}const xB=/[ \t\n\f\r]/g;function vB(t){return typeof t=="object"?t.type==="text"?fw(t.value):!1:fw(t)}function fw(t){return t.replace(xB,"")===""}let Mc=class{constructor(e,n,r){this.normal=n,this.property=e,r&&(this.space=r)}};Mc.prototype.normal={};Mc.prototype.property={};Mc.prototype.space=void 0;function KA(t,e){const n={},r={};for(const i of t)Object.assign(n,i.property),Object.assign(r,i.normal);return new Mc(n,r,e)}function fc(t){return t.toLowerCase()}let Mr=class{constructor(e,n){this.attribute=n,this.property=e}};Mr.prototype.attribute="";Mr.prototype.booleanish=!1;Mr.prototype.boolean=!1;Mr.prototype.commaOrSpaceSeparated=!1;Mr.prototype.commaSeparated=!1;Mr.prototype.defined=!1;Mr.prototype.mustUseProperty=!1;Mr.prototype.number=!1;Mr.prototype.overloadedBoolean=!1;Mr.prototype.property="";Mr.prototype.spaceSeparated=!1;Mr.prototype.space=void 0;let wB=0;const pt=aa(),Nn=aa(),G0=aa(),Te=aa(),Zt=aa(),ol=aa(),Ur=aa();function aa(){return 2**++wB}const K0=Object.freeze(Object.defineProperty({__proto__:null,boolean:pt,booleanish:Nn,commaOrSpaceSeparated:Ur,commaSeparated:ol,number:Te,overloadedBoolean:G0,spaceSeparated:Zt},Symbol.toStringTag,{value:"Module"})),Pg=Object.keys(K0);let jE=class extends Mr{constructor(e,n,r,i){let s=-1;if(super(e,n),hw(this,"space",i),typeof r=="number")for(;++s4&&n.slice(0,4)==="data"&&AB.test(e)){if(e.charAt(4)==="-"){const s=e.slice(5).replace(pw,NB);r="data"+s.charAt(0).toUpperCase()+s.slice(1)}else{const s=e.slice(4);if(!pw.test(s)){let o=s.replace(CB,kB);o.charAt(0)!=="-"&&(o="-"+o),e="data"+o}}i=jE}return new i(r,e)}function kB(t){return"-"+t.toLowerCase()}function NB(t){return t.charAt(1).toUpperCase()}const sp=KA([YA,TB,QA,ZA,JA],"html"),Ml=KA([YA,SB,QA,ZA,JA],"svg");function mw(t){const e=String(t||"").trim();return e?e.split(/[ \t\n\r\f]+/g):[]}function ek(t){return t.join(" ").trim()}var za={},Fg,gw;function RB(){if(gw)return Fg;gw=1;var t=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g,e=/\n/g,n=/^\s*/,r=/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/,i=/^:\s*/,s=/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/,o=/^[;\s]*/,l=/^\s+|\s+$/g,c=` `,d="/",f="*",p="",m="comment",g="declaration";Fg=function(v,S){if(typeof v!="string")throw new TypeError("First argument must be a string");if(!v)return[];S=S||{};var C=1,A=1;function k(ie){var Z=ie.match(e);Z&&(C+=Z.length);var ee=ie.lastIndexOf(c);A=~ee?ie.length-ee:A+ie.length}function M(){var ie={line:C,column:A};return function(Z){return Z.position=new F(ie),G(),Z}}function F(ie){this.start=ie,this.end={line:C,column:A},this.source=S.source}F.prototype.content=v;function I(ie){var Z=new Error(S.source+":"+C+":"+A+": "+ie);if(Z.reason=ie,Z.filename=S.source,Z.line=C,Z.column=A,Z.source=v,!S.silent)throw Z}function D(ie){var Z=ie.exec(v);if(Z){var ee=Z[0];return k(ee),v=v.slice(ee.length),Z}}function G(){D(n)}function X(ie){var Z;for(ie=ie||[];Z=P();)Z!==!1&&ie.push(Z);return ie}function P(){var ie=M();if(!(d!=v.charAt(0)||f!=v.charAt(1))){for(var Z=2;p!=v.charAt(Z)&&(f!=v.charAt(Z)||d!=v.charAt(Z+1));)++Z;if(Z+=2,p===v.charAt(Z-1))return I("End of comment missing");var ee=v.slice(2,Z-2);return A+=2,k(ee),v=v.slice(Z),A+=2,ie({type:m,comment:ee})}}function Y(){var ie=M(),Z=D(r);if(Z){if(P(),!D(i))return I("property missing ':'");var ee=D(s),ae=ie({type:g,property:x(Z[0].replace(t,p)),value:ee?x(ee[0].replace(t,p)):p});return D(o),ae}}function z(){var ie=[];X(ie);for(var Z;Z=Y();)Z!==!1&&(ie.push(Z),X(ie));return ie}return G(),z()};function x(v){return v?v.replace(l,p):p}return Fg}var bw;function IB(){if(bw)return za;bw=1;var t=za&&za.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(za,"__esModule",{value:!0}),za.default=n;var e=t(RB());function n(r,i){var s=null;if(!r||typeof r!="string")return s;var o=(0,e.default)(r),l=typeof i=="function";return o.forEach(function(c){if(c.type==="declaration"){var d=c.property,f=c.value;l?i(d,f,c):f&&(s=s||{},s[d]=f)}}),s}return za}var ku={},Ew;function OB(){if(Ew)return ku;Ew=1,Object.defineProperty(ku,"__esModule",{value:!0}),ku.camelCase=void 0;var t=/^--[a-zA-Z0-9_-]+$/,e=/-([a-z])/g,n=/^[^-]+$/,r=/^-(webkit|moz|ms|o|khtml)-/,i=/^-(ms)-/,s=function(d){return!d||n.test(d)||t.test(d)},o=function(d,f){return f.toUpperCase()},l=function(d,f){return"".concat(f,"-")},c=function(d,f){return f===void 0&&(f={}),s(d)?d:(d=d.toLowerCase(),f.reactCompat?d=d.replace(i,l):d=d.replace(r,l),d.replace(e,o))};return ku.camelCase=c,ku}var Nu,yw;function MB(){if(yw)return Nu;yw=1;var t=Nu&&Nu.__importDefault||function(i){return i&&i.__esModule?i:{default:i}},e=t(IB()),n=OB();function r(i,s){var o={};return!i||typeof i!="string"||(0,e.default)(i,function(l,c){l&&c&&(o[(0,n.camelCase)(l,s)]=c)}),o}return r.default=r,Nu=r,Nu}var DB=MB();const LB=_s(DB),op=tk("end"),Ji=tk("start");function tk(t){return e;function e(n){const r=n&&n.position&&n.position[t]||{};if(typeof r.line=="number"&&r.line>0&&typeof r.column=="number"&&r.column>0)return{line:r.line,column:r.column,offset:typeof r.offset=="number"&&r.offset>-1?r.offset:void 0}}}function PB(t){const e=Ji(t),n=op(t);if(e&&n)return{start:e,end:n}}function Gu(t){return!t||typeof t!="object"?"":"position"in t||"type"in t?xw(t.position):"start"in t||"end"in t?xw(t):"line"in t||"column"in t?Y0(t):""}function Y0(t){return vw(t&&t.line)+":"+vw(t&&t.column)}function xw(t){return Y0(t&&t.start)+"-"+Y0(t&&t.end)}function vw(t){return t&&typeof t=="number"?t:1}class ur extends Error{constructor(e,n,r){super(),typeof n=="string"&&(r=n,n=void 0);let i="",s={},o=!1;if(n&&("line"in n&&"column"in n?s={place:n}:"start"in n&&"end"in n?s={place:n}:"type"in n?s={ancestors:[n],place:n.position}:s={...n}),typeof e=="string"?i=e:!s.cause&&e&&(o=!0,i=e.message,s.cause=e),!s.ruleId&&!s.source&&typeof r=="string"){const c=r.indexOf(":");c===-1?s.ruleId=r:(s.source=r.slice(0,c),s.ruleId=r.slice(c+1))}if(!s.place&&s.ancestors&&s.ancestors){const c=s.ancestors[s.ancestors.length-1];c&&(s.place=c.position)}const l=s.place&&"start"in s.place?s.place.start:s.place;this.ancestors=s.ancestors||void 0,this.cause=s.cause||void 0,this.column=l?l.column:void 0,this.fatal=void 0,this.file="",this.message=i,this.line=l?l.line:void 0,this.name=Gu(s.place)||"1:1",this.place=s.place||void 0,this.reason=this.message,this.ruleId=s.ruleId||void 0,this.source=s.source||void 0,this.stack=o&&s.cause&&typeof s.cause.stack=="string"?s.cause.stack:"",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}}ur.prototype.file="";ur.prototype.name="";ur.prototype.reason="";ur.prototype.message="";ur.prototype.stack="";ur.prototype.column=void 0;ur.prototype.line=void 0;ur.prototype.ancestors=void 0;ur.prototype.cause=void 0;ur.prototype.fatal=void 0;ur.prototype.place=void 0;ur.prototype.ruleId=void 0;ur.prototype.source=void 0;const WE={}.hasOwnProperty,FB=new Map,BB=/[A-Z]/g,UB=new Set(["table","tbody","thead","tfoot","tr"]),HB=new Set(["td","th"]),nk="https://github.com/syntax-tree/hast-util-to-jsx-runtime";function zB(t,e){if(!e||e.Fragment===void 0)throw new TypeError("Expected `Fragment` in options");const n=e.filePath||void 0;let r;if(e.development){if(typeof e.jsxDEV!="function")throw new TypeError("Expected `jsxDEV` in options when `development: true`");r=qB(n,e.jsxDEV)}else{if(typeof e.jsx!="function")throw new TypeError("Expected `jsx` in production options");if(typeof e.jsxs!="function")throw new TypeError("Expected `jsxs` in production options");r=YB(n,e.jsx,e.jsxs)}const i={Fragment:e.Fragment,ancestors:[],components:e.components||{},create:r,elementAttributeNameCase:e.elementAttributeNameCase||"react",evaluater:e.createEvaluater?e.createEvaluater():void 0,filePath:n,ignoreInvalidStyle:e.ignoreInvalidStyle||!1,passKeys:e.passKeys!==!1,passNode:e.passNode||!1,schema:e.space==="svg"?Ml:sp,stylePropertyNameCase:e.stylePropertyNameCase||"dom",tableCellAlignToStyle:e.tableCellAlignToStyle!==!1},s=rk(i,t,void 0);return s&&typeof s!="string"?s:i.create(t,i.Fragment,{children:s||void 0},void 0)}function rk(t,e,n){if(e.type==="element")return jB(t,e,n);if(e.type==="mdxFlowExpression"||e.type==="mdxTextExpression")return $B(t,e);if(e.type==="mdxJsxFlowElement"||e.type==="mdxJsxTextElement")return VB(t,e,n);if(e.type==="mdxjsEsm")return WB(t,e);if(e.type==="root")return GB(t,e,n);if(e.type==="text")return KB(t,e)}function jB(t,e,n){const r=t.schema;let i=r;e.tagName.toLowerCase()==="svg"&&r.space==="html"&&(i=Ml,t.schema=i),t.ancestors.push(e);const s=sk(t,e.tagName,!1),o=XB(t,e);let l=GE(t,e);return UB.has(e.tagName)&&(l=l.filter(function(c){return typeof c=="string"?!vB(c):!0})),ik(t,o,s,e),VE(o,l),t.ancestors.pop(),t.schema=r,t.create(e,s,o,n)}function $B(t,e){if(e.data&&e.data.estree&&t.evaluater){const r=e.data.estree.body[0];return r.type,t.evaluater.evaluateExpression(r.expression)}hc(t,e.position)}function WB(t,e){if(e.data&&e.data.estree&&t.evaluater)return t.evaluater.evaluateProgram(e.data.estree);hc(t,e.position)}function VB(t,e,n){const r=t.schema;let i=r;e.name==="svg"&&r.space==="html"&&(i=Ml,t.schema=i),t.ancestors.push(e);const s=e.name===null?t.Fragment:sk(t,e.name,!0),o=QB(t,e),l=GE(t,e);return ik(t,o,s,e),VE(o,l),t.ancestors.pop(),t.schema=r,t.create(e,s,o,n)}function GB(t,e,n){const r={};return VE(r,GE(t,e)),t.create(e,t.Fragment,r,n)}function KB(t,e){return e.value}function ik(t,e,n,r){typeof n!="string"&&n!==t.Fragment&&t.passNode&&(e.node=r)}function VE(t,e){if(e.length>0){const n=e.length>1?e:e[0];n&&(t.children=n)}}function YB(t,e,n){return r;function r(i,s,o,l){const d=Array.isArray(o.children)?n:e;return l?d(s,o,l):d(s,o)}}function qB(t,e){return n;function n(r,i,s,o){const l=Array.isArray(s.children),c=Ji(r);return e(i,s,o,l,{columnNumber:c?c.column-1:void 0,fileName:t,lineNumber:c?c.line:void 0},void 0)}}function XB(t,e){const n={};let r,i;for(i in e.properties)if(i!=="children"&&WE.call(e.properties,i)){const s=ZB(t,i,e.properties[i]);if(s){const[o,l]=s;t.tableCellAlignToStyle&&o==="align"&&typeof l=="string"&&HB.has(e.tagName)?r=l:n[o]=l}}if(r){const s=n.style||(n.style={});s[t.stylePropertyNameCase==="css"?"text-align":"textAlign"]=r}return n}function QB(t,e){const n={};for(const r of e.attributes)if(r.type==="mdxJsxExpressionAttribute")if(r.data&&r.data.estree&&t.evaluater){const s=r.data.estree.body[0];s.type;const o=s.expression;o.type;const l=o.properties[0];l.type,Object.assign(n,t.evaluater.evaluateExpression(l.argument))}else hc(t,e.position);else{const i=r.name;let s;if(r.value&&typeof r.value=="object")if(r.value.data&&r.value.data.estree&&t.evaluater){const l=r.value.data.estree.body[0];l.type,s=t.evaluater.evaluateExpression(l.expression)}else hc(t,e.position);else s=r.value===null?!0:r.value;n[i]=s}return n}function GE(t,e){const n=[];let r=-1;const i=t.passKeys?new Map:FB;for(;++ri?0:i+e:e=e>i?i:e,n=n>0?n:0,r.length<1e4)o=Array.from(r),o.unshift(e,n),t.splice(...o);else for(n&&t.splice(e,n);s0?(Gr(t,t.length,0,e),t):e}const Sw={}.hasOwnProperty;function ak(t){const e={};let n=-1;for(;++n13&&n<32||n>126&&n<160||n>55295&&n<57344||n>64975&&n<65008||(n&65535)===65535||(n&65535)===65534||n>1114111?"�":String.fromCodePoint(n)}function vi(t){return t.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}const Er=po(/[A-Za-z]/),sr=po(/[\dA-Za-z]/),a5=po(/[#-'*+\--9=?A-Z^-~]/);function uh(t){return t!==null&&(t<32||t===127)}const q0=po(/\d/),l5=po(/[\dA-Fa-f]/),u5=po(/[!-/:-@[-`{-~]/);function et(t){return t!==null&&t<-2}function Kt(t){return t!==null&&(t<0||t===32)}function St(t){return t===-2||t===-1||t===32}const ap=po(new RegExp("\\p{P}|\\p{S}","u")),Xo=po(/\s/);function po(t){return e;function e(n){return n!==null&&n>-1&&t.test(String.fromCharCode(n))}}function Dl(t){const e=[];let n=-1,r=0,i=0;for(;++n55295&&s<57344){const l=t.charCodeAt(n+1);s<56320&&l>56319&&l<57344?(o=String.fromCharCode(s,l),i=1):o="�"}else o=String.fromCharCode(s);o&&(e.push(t.slice(r,n),encodeURIComponent(o)),r=n+i+1,o=""),i&&(n+=i,i=0)}return e.join("")+t.slice(r)}function Nt(t,e,n,r){const i=r?r-1:Number.POSITIVE_INFINITY;let s=0;return o;function o(c){return St(c)?(t.enter(n),l(c)):e(c)}function l(c){return St(c)&&s++o))return;const D=e.events.length;let G=D,X,P;for(;G--;)if(e.events[G][0]==="exit"&&e.events[G][1].type==="chunkFlow"){if(X){P=e.events[G][1].end;break}X=!0}for(C(r),I=D;Ik;){const F=n[M];e.containerState=F[1],F[0].exit.call(e,t)}n.length=k}function A(){i.write([null]),s=void 0,i=void 0,e.containerState._closeFlow=void 0}}function p5(t,e,n){return Nt(t,t.attempt(this.parser.constructs.document,e,n),"linePrefix",this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function bl(t){if(t===null||Kt(t)||Xo(t))return 1;if(ap(t))return 2}function lp(t,e,n){const r=[];let i=-1;for(;++i1&&t[n][1].end.offset-t[n][1].start.offset>1?2:1;const p={...t[r][1].end},m={...t[n][1].start};Cw(p,-c),Cw(m,c),o={type:c>1?"strongSequence":"emphasisSequence",start:p,end:{...t[r][1].end}},l={type:c>1?"strongSequence":"emphasisSequence",start:{...t[n][1].start},end:m},s={type:c>1?"strongText":"emphasisText",start:{...t[r][1].end},end:{...t[n][1].start}},i={type:c>1?"strong":"emphasis",start:{...o.start},end:{...l.end}},t[r][1].end={...o.start},t[n][1].start={...l.end},d=[],t[r][1].end.offset-t[r][1].start.offset&&(d=ti(d,[["enter",t[r][1],e],["exit",t[r][1],e]])),d=ti(d,[["enter",i,e],["enter",o,e],["exit",o,e],["enter",s,e]]),d=ti(d,lp(e.parser.constructs.insideSpan.null,t.slice(r+1,n),e)),d=ti(d,[["exit",s,e],["enter",l,e],["exit",l,e],["exit",i,e]]),t[n][1].end.offset-t[n][1].start.offset?(f=2,d=ti(d,[["enter",t[n][1],e],["exit",t[n][1],e]])):f=0,Gr(t,r-1,n-r+3,d),n=r+d.length-f-2;break}}for(n=-1;++n0&&St(I)?Nt(t,A,"linePrefix",s+1)(I):A(I)}function A(I){return I===null||et(I)?t.check(Aw,v,M)(I):(t.enter("codeFlowValue"),k(I))}function k(I){return I===null||et(I)?(t.exit("codeFlowValue"),A(I)):(t.consume(I),k)}function M(I){return t.exit("codeFenced"),e(I)}function F(I,D,G){let X=0;return P;function P(ee){return I.enter("lineEnding"),I.consume(ee),I.exit("lineEnding"),Y}function Y(ee){return I.enter("codeFencedFence"),St(ee)?Nt(I,z,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(ee):z(ee)}function z(ee){return ee===l?(I.enter("codeFencedFenceSequence"),ie(ee)):G(ee)}function ie(ee){return ee===l?(X++,I.consume(ee),ie):X>=o?(I.exit("codeFencedFenceSequence"),St(ee)?Nt(I,Z,"whitespace")(ee):Z(ee)):G(ee)}function Z(ee){return ee===null||et(ee)?(I.exit("codeFencedFence"),D(ee)):G(ee)}}}function C5(t,e,n){const r=this;return i;function i(o){return o===null?n(o):(t.enter("lineEnding"),t.consume(o),t.exit("lineEnding"),s)}function s(o){return r.parser.lazy[r.now().line]?n(o):e(o)}}const Ug={name:"codeIndented",tokenize:k5},A5={partial:!0,tokenize:N5};function k5(t,e,n){const r=this;return i;function i(d){return t.enter("codeIndented"),Nt(t,s,"linePrefix",5)(d)}function s(d){const f=r.events[r.events.length-1];return f&&f[1].type==="linePrefix"&&f[2].sliceSerialize(f[1],!0).length>=4?o(d):n(d)}function o(d){return d===null?c(d):et(d)?t.attempt(A5,o,c)(d):(t.enter("codeFlowValue"),l(d))}function l(d){return d===null||et(d)?(t.exit("codeFlowValue"),o(d)):(t.consume(d),l)}function c(d){return t.exit("codeIndented"),e(d)}}function N5(t,e,n){const r=this;return i;function i(o){return r.parser.lazy[r.now().line]?n(o):et(o)?(t.enter("lineEnding"),t.consume(o),t.exit("lineEnding"),i):Nt(t,s,"linePrefix",5)(o)}function s(o){const l=r.events[r.events.length-1];return l&&l[1].type==="linePrefix"&&l[2].sliceSerialize(l[1],!0).length>=4?e(o):et(o)?i(o):n(o)}}const R5={name:"codeText",previous:O5,resolve:I5,tokenize:M5};function I5(t){let e=t.length-4,n=3,r,i;if((t[n][1].type==="lineEnding"||t[n][1].type==="space")&&(t[e][1].type==="lineEnding"||t[e][1].type==="space")){for(r=n;++r=this.left.length+this.right.length)throw new RangeError("Cannot access index `"+e+"` in a splice buffer of size `"+(this.left.length+this.right.length)+"`");return ethis.left.length?this.right.slice(this.right.length-r+this.left.length,this.right.length-e+this.left.length).reverse():this.left.slice(e).concat(this.right.slice(this.right.length-r+this.left.length).reverse())}splice(e,n,r){const i=n||0;this.setCursor(Math.trunc(e));const s=this.right.splice(this.right.length-i,Number.POSITIVE_INFINITY);return r&&Ru(this.left,r),s.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(e){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(e)}pushMany(e){this.setCursor(Number.POSITIVE_INFINITY),Ru(this.left,e)}unshift(e){this.setCursor(0),this.right.push(e)}unshiftMany(e){this.setCursor(0),Ru(this.right,e.reverse())}setCursor(e){if(!(e===this.left.length||e>this.left.length&&this.right.length===0||e<0&&this.left.length===0))if(e=4?e(o):t.interrupt(r.parser.constructs.flow,n,e)(o)}}function hk(t,e,n,r,i,s,o,l,c){const d=c||Number.POSITIVE_INFINITY;let f=0;return p;function p(C){return C===60?(t.enter(r),t.enter(i),t.enter(s),t.consume(C),t.exit(s),m):C===null||C===32||C===41||uh(C)?n(C):(t.enter(r),t.enter(o),t.enter(l),t.enter("chunkString",{contentType:"string"}),v(C))}function m(C){return C===62?(t.enter(s),t.consume(C),t.exit(s),t.exit(i),t.exit(r),e):(t.enter(l),t.enter("chunkString",{contentType:"string"}),g(C))}function g(C){return C===62?(t.exit("chunkString"),t.exit(l),m(C)):C===null||C===60||et(C)?n(C):(t.consume(C),C===92?x:g)}function x(C){return C===60||C===62||C===92?(t.consume(C),g):g(C)}function v(C){return!f&&(C===null||C===41||Kt(C))?(t.exit("chunkString"),t.exit(l),t.exit(o),t.exit(r),e(C)):f999||g===null||g===91||g===93&&!c||g===94&&!l&&"_hiddenFootnoteSupport"in o.parser.constructs?n(g):g===93?(t.exit(s),t.enter(i),t.consume(g),t.exit(i),t.exit(r),e):et(g)?(t.enter("lineEnding"),t.consume(g),t.exit("lineEnding"),f):(t.enter("chunkString",{contentType:"string"}),p(g))}function p(g){return g===null||g===91||g===93||et(g)||l++>999?(t.exit("chunkString"),f(g)):(t.consume(g),c||(c=!St(g)),g===92?m:p)}function m(g){return g===91||g===92||g===93?(t.consume(g),l++,p):p(g)}}function mk(t,e,n,r,i,s){let o;return l;function l(m){return m===34||m===39||m===40?(t.enter(r),t.enter(i),t.consume(m),t.exit(i),o=m===40?41:m,c):n(m)}function c(m){return m===o?(t.enter(i),t.consume(m),t.exit(i),t.exit(r),e):(t.enter(s),d(m))}function d(m){return m===o?(t.exit(s),c(o)):m===null?n(m):et(m)?(t.enter("lineEnding"),t.consume(m),t.exit("lineEnding"),Nt(t,d,"linePrefix")):(t.enter("chunkString",{contentType:"string"}),f(m))}function f(m){return m===o||m===null||et(m)?(t.exit("chunkString"),d(m)):(t.consume(m),m===92?p:f)}function p(m){return m===o||m===92?(t.consume(m),f):f(m)}}function Ku(t,e){let n;return r;function r(i){return et(i)?(t.enter("lineEnding"),t.consume(i),t.exit("lineEnding"),n=!0,r):St(i)?Nt(t,r,n?"linePrefix":"lineSuffix")(i):e(i)}}const z5={name:"definition",tokenize:$5},j5={partial:!0,tokenize:W5};function $5(t,e,n){const r=this;let i;return s;function s(g){return t.enter("definition"),o(g)}function o(g){return pk.call(r,t,l,n,"definitionLabel","definitionLabelMarker","definitionLabelString")(g)}function l(g){return i=vi(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)),g===58?(t.enter("definitionMarker"),t.consume(g),t.exit("definitionMarker"),c):n(g)}function c(g){return Kt(g)?Ku(t,d)(g):d(g)}function d(g){return hk(t,f,n,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(g)}function f(g){return t.attempt(j5,p,p)(g)}function p(g){return St(g)?Nt(t,m,"whitespace")(g):m(g)}function m(g){return g===null||et(g)?(t.exit("definition"),r.parser.defined.push(i),e(g)):n(g)}}function W5(t,e,n){return r;function r(l){return Kt(l)?Ku(t,i)(l):n(l)}function i(l){return mk(t,s,n,"definitionTitle","definitionTitleMarker","definitionTitleString")(l)}function s(l){return St(l)?Nt(t,o,"whitespace")(l):o(l)}function o(l){return l===null||et(l)?e(l):n(l)}}const V5={name:"hardBreakEscape",tokenize:G5};function G5(t,e,n){return r;function r(s){return t.enter("hardBreakEscape"),t.consume(s),i}function i(s){return et(s)?(t.exit("hardBreakEscape"),e(s)):n(s)}}const K5={name:"headingAtx",resolve:Y5,tokenize:q5};function Y5(t,e){let n=t.length-2,r=3,i,s;return t[r][1].type==="whitespace"&&(r+=2),n-2>r&&t[n][1].type==="whitespace"&&(n-=2),t[n][1].type==="atxHeadingSequence"&&(r===n-1||n-4>r&&t[n-2][1].type==="whitespace")&&(n-=r+1===n?2:4),n>r&&(i={type:"atxHeadingText",start:t[r][1].start,end:t[n][1].end},s={type:"chunkText",start:t[r][1].start,end:t[n][1].end,contentType:"text"},Gr(t,r,n-r+1,[["enter",i,e],["enter",s,e],["exit",s,e],["exit",i,e]])),t}function q5(t,e,n){let r=0;return i;function i(f){return t.enter("atxHeading"),s(f)}function s(f){return t.enter("atxHeadingSequence"),o(f)}function o(f){return f===35&&r++<6?(t.consume(f),o):f===null||Kt(f)?(t.exit("atxHeadingSequence"),l(f)):n(f)}function l(f){return f===35?(t.enter("atxHeadingSequence"),c(f)):f===null||et(f)?(t.exit("atxHeading"),e(f)):St(f)?Nt(t,l,"whitespace")(f):(t.enter("atxHeadingText"),d(f))}function c(f){return f===35?(t.consume(f),c):(t.exit("atxHeadingSequence"),l(f))}function d(f){return f===null||f===35||Kt(f)?(t.exit("atxHeadingText"),l(f)):(t.consume(f),d)}}const X5=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","search","section","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],Nw=["pre","script","style","textarea"],Q5={concrete:!0,name:"htmlFlow",resolveTo:e8,tokenize:t8},Z5={partial:!0,tokenize:r8},J5={partial:!0,tokenize:n8};function e8(t){let e=t.length;for(;e--&&!(t[e][0]==="enter"&&t[e][1].type==="htmlFlow"););return e>1&&t[e-2][1].type==="linePrefix"&&(t[e][1].start=t[e-2][1].start,t[e+1][1].start=t[e-2][1].start,t.splice(e-2,2)),t}function t8(t,e,n){const r=this;let i,s,o,l,c;return d;function d(R){return f(R)}function f(R){return t.enter("htmlFlow"),t.enter("htmlFlowData"),t.consume(R),p}function p(R){return R===33?(t.consume(R),m):R===47?(t.consume(R),s=!0,v):R===63?(t.consume(R),i=3,r.interrupt?e:O):Er(R)?(t.consume(R),o=String.fromCharCode(R),S):n(R)}function m(R){return R===45?(t.consume(R),i=2,g):R===91?(t.consume(R),i=5,l=0,x):Er(R)?(t.consume(R),i=4,r.interrupt?e:O):n(R)}function g(R){return R===45?(t.consume(R),r.interrupt?e:O):n(R)}function x(R){const oe="CDATA[";return R===oe.charCodeAt(l++)?(t.consume(R),l===oe.length?r.interrupt?e:z:x):n(R)}function v(R){return Er(R)?(t.consume(R),o=String.fromCharCode(R),S):n(R)}function S(R){if(R===null||R===47||R===62||Kt(R)){const oe=R===47,pe=o.toLowerCase();return!oe&&!s&&Nw.includes(pe)?(i=1,r.interrupt?e(R):z(R)):X5.includes(o.toLowerCase())?(i=6,oe?(t.consume(R),C):r.interrupt?e(R):z(R)):(i=7,r.interrupt&&!r.parser.lazy[r.now().line]?n(R):s?A(R):k(R))}return R===45||sr(R)?(t.consume(R),o+=String.fromCharCode(R),S):n(R)}function C(R){return R===62?(t.consume(R),r.interrupt?e:z):n(R)}function A(R){return St(R)?(t.consume(R),A):P(R)}function k(R){return R===47?(t.consume(R),P):R===58||R===95||Er(R)?(t.consume(R),M):St(R)?(t.consume(R),k):P(R)}function M(R){return R===45||R===46||R===58||R===95||sr(R)?(t.consume(R),M):F(R)}function F(R){return R===61?(t.consume(R),I):St(R)?(t.consume(R),F):k(R)}function I(R){return R===null||R===60||R===61||R===62||R===96?n(R):R===34||R===39?(t.consume(R),c=R,D):St(R)?(t.consume(R),I):G(R)}function D(R){return R===c?(t.consume(R),c=null,X):R===null||et(R)?n(R):(t.consume(R),D)}function G(R){return R===null||R===34||R===39||R===47||R===60||R===61||R===62||R===96||Kt(R)?F(R):(t.consume(R),G)}function X(R){return R===47||R===62||St(R)?k(R):n(R)}function P(R){return R===62?(t.consume(R),Y):n(R)}function Y(R){return R===null||et(R)?z(R):St(R)?(t.consume(R),Y):n(R)}function z(R){return R===45&&i===2?(t.consume(R),ae):R===60&&i===1?(t.consume(R),de):R===62&&i===4?(t.consume(R),U):R===63&&i===3?(t.consume(R),O):R===93&&i===5?(t.consume(R),W):et(R)&&(i===6||i===7)?(t.exit("htmlFlowData"),t.check(Z5,Q,ie)(R)):R===null||et(R)?(t.exit("htmlFlowData"),ie(R)):(t.consume(R),z)}function ie(R){return t.check(J5,Z,Q)(R)}function Z(R){return t.enter("lineEnding"),t.consume(R),t.exit("lineEnding"),ee}function ee(R){return R===null||et(R)?ie(R):(t.enter("htmlFlowData"),z(R))}function ae(R){return R===45?(t.consume(R),O):z(R)}function de(R){return R===47?(t.consume(R),o="",j):z(R)}function j(R){if(R===62){const oe=o.toLowerCase();return Nw.includes(oe)?(t.consume(R),U):z(R)}return Er(R)&&o.length<8?(t.consume(R),o+=String.fromCharCode(R),j):z(R)}function W(R){return R===93?(t.consume(R),O):z(R)}function O(R){return R===62?(t.consume(R),U):R===45&&i===2?(t.consume(R),O):z(R)}function U(R){return R===null||et(R)?(t.exit("htmlFlowData"),Q(R)):(t.consume(R),U)}function Q(R){return t.exit("htmlFlow"),e(R)}}function n8(t,e,n){const r=this;return i;function i(o){return et(o)?(t.enter("lineEnding"),t.consume(o),t.exit("lineEnding"),s):n(o)}function s(o){return r.parser.lazy[r.now().line]?n(o):e(o)}}function r8(t,e,n){return r;function r(i){return t.enter("lineEnding"),t.consume(i),t.exit("lineEnding"),t.attempt(Dc,e,n)}}const i8={name:"htmlText",tokenize:s8};function s8(t,e,n){const r=this;let i,s,o;return l;function l(O){return t.enter("htmlText"),t.enter("htmlTextData"),t.consume(O),c}function c(O){return O===33?(t.consume(O),d):O===47?(t.consume(O),F):O===63?(t.consume(O),k):Er(O)?(t.consume(O),G):n(O)}function d(O){return O===45?(t.consume(O),f):O===91?(t.consume(O),s=0,x):Er(O)?(t.consume(O),A):n(O)}function f(O){return O===45?(t.consume(O),g):n(O)}function p(O){return O===null?n(O):O===45?(t.consume(O),m):et(O)?(o=p,de(O)):(t.consume(O),p)}function m(O){return O===45?(t.consume(O),g):p(O)}function g(O){return O===62?ae(O):O===45?m(O):p(O)}function x(O){const U="CDATA[";return O===U.charCodeAt(s++)?(t.consume(O),s===U.length?v:x):n(O)}function v(O){return O===null?n(O):O===93?(t.consume(O),S):et(O)?(o=v,de(O)):(t.consume(O),v)}function S(O){return O===93?(t.consume(O),C):v(O)}function C(O){return O===62?ae(O):O===93?(t.consume(O),C):v(O)}function A(O){return O===null||O===62?ae(O):et(O)?(o=A,de(O)):(t.consume(O),A)}function k(O){return O===null?n(O):O===63?(t.consume(O),M):et(O)?(o=k,de(O)):(t.consume(O),k)}function M(O){return O===62?ae(O):k(O)}function F(O){return Er(O)?(t.consume(O),I):n(O)}function I(O){return O===45||sr(O)?(t.consume(O),I):D(O)}function D(O){return et(O)?(o=D,de(O)):St(O)?(t.consume(O),D):ae(O)}function G(O){return O===45||sr(O)?(t.consume(O),G):O===47||O===62||Kt(O)?X(O):n(O)}function X(O){return O===47?(t.consume(O),ae):O===58||O===95||Er(O)?(t.consume(O),P):et(O)?(o=X,de(O)):St(O)?(t.consume(O),X):ae(O)}function P(O){return O===45||O===46||O===58||O===95||sr(O)?(t.consume(O),P):Y(O)}function Y(O){return O===61?(t.consume(O),z):et(O)?(o=Y,de(O)):St(O)?(t.consume(O),Y):X(O)}function z(O){return O===null||O===60||O===61||O===62||O===96?n(O):O===34||O===39?(t.consume(O),i=O,ie):et(O)?(o=z,de(O)):St(O)?(t.consume(O),z):(t.consume(O),Z)}function ie(O){return O===i?(t.consume(O),i=void 0,ee):O===null?n(O):et(O)?(o=ie,de(O)):(t.consume(O),ie)}function Z(O){return O===null||O===34||O===39||O===60||O===61||O===96?n(O):O===47||O===62||Kt(O)?X(O):(t.consume(O),Z)}function ee(O){return O===47||O===62||Kt(O)?X(O):n(O)}function ae(O){return O===62?(t.consume(O),t.exit("htmlTextData"),t.exit("htmlText"),e):n(O)}function de(O){return t.exit("htmlTextData"),t.enter("lineEnding"),t.consume(O),t.exit("lineEnding"),j}function j(O){return St(O)?Nt(t,W,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(O):W(O)}function W(O){return t.enter("htmlTextData"),o(O)}}const qE={name:"labelEnd",resolveAll:u8,resolveTo:c8,tokenize:d8},o8={tokenize:f8},a8={tokenize:h8},l8={tokenize:p8};function u8(t){let e=-1;const n=[];for(;++e=3&&(d===null||et(d))?(t.exit("thematicBreak"),e(d)):n(d)}function c(d){return d===i?(t.consume(d),r++,c):(t.exit("thematicBreakSequence"),St(d)?Nt(t,l,"whitespace")(d):l(d))}}const Nr={continuation:{tokenize:S8},exit:C8,name:"list",tokenize:T8},v8={partial:!0,tokenize:A8},w8={partial:!0,tokenize:_8};function T8(t,e,n){const r=this,i=r.events[r.events.length-1];let s=i&&i[1].type==="linePrefix"?i[2].sliceSerialize(i[1],!0).length:0,o=0;return l;function l(g){const x=r.containerState.type||(g===42||g===43||g===45?"listUnordered":"listOrdered");if(x==="listUnordered"?!r.containerState.marker||g===r.containerState.marker:q0(g)){if(r.containerState.type||(r.containerState.type=x,t.enter(x,{_container:!0})),x==="listUnordered")return t.enter("listItemPrefix"),g===42||g===45?t.check(Bf,n,d)(g):d(g);if(!r.interrupt||g===49)return t.enter("listItemPrefix"),t.enter("listItemValue"),c(g)}return n(g)}function c(g){return q0(g)&&++o<10?(t.consume(g),c):(!r.interrupt||o<2)&&(r.containerState.marker?g===r.containerState.marker:g===41||g===46)?(t.exit("listItemValue"),d(g)):n(g)}function d(g){return t.enter("listItemMarker"),t.consume(g),t.exit("listItemMarker"),r.containerState.marker=r.containerState.marker||g,t.check(Dc,r.interrupt?n:f,t.attempt(v8,m,p))}function f(g){return r.containerState.initialBlankLine=!0,s++,m(g)}function p(g){return St(g)?(t.enter("listItemPrefixWhitespace"),t.consume(g),t.exit("listItemPrefixWhitespace"),m):n(g)}function m(g){return r.containerState.size=s+r.sliceSerialize(t.exit("listItemPrefix"),!0).length,e(g)}}function S8(t,e,n){const r=this;return r.containerState._closeFlow=void 0,t.check(Dc,i,s);function i(l){return r.containerState.furtherBlankLines=r.containerState.furtherBlankLines||r.containerState.initialBlankLine,Nt(t,e,"listItemIndent",r.containerState.size+1)(l)}function s(l){return r.containerState.furtherBlankLines||!St(l)?(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,o(l)):(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,t.attempt(w8,e,o)(l))}function o(l){return r.containerState._closeFlow=!0,r.interrupt=void 0,Nt(t,t.attempt(Nr,e,n),"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(l)}}function _8(t,e,n){const r=this;return Nt(t,i,"listItemIndent",r.containerState.size+1);function i(s){const o=r.events[r.events.length-1];return o&&o[1].type==="listItemIndent"&&o[2].sliceSerialize(o[1],!0).length===r.containerState.size?e(s):n(s)}}function C8(t){t.exit(this.containerState.type)}function A8(t,e,n){const r=this;return Nt(t,i,"listItemPrefixWhitespace",r.parser.constructs.disable.null.includes("codeIndented")?void 0:5);function i(s){const o=r.events[r.events.length-1];return!St(s)&&o&&o[1].type==="listItemPrefixWhitespace"?e(s):n(s)}}const Rw={name:"setextUnderline",resolveTo:k8,tokenize:N8};function k8(t,e){let n=t.length,r,i,s;for(;n--;)if(t[n][0]==="enter"){if(t[n][1].type==="content"){r=n;break}t[n][1].type==="paragraph"&&(i=n)}else t[n][1].type==="content"&&t.splice(n,1),!s&&t[n][1].type==="definition"&&(s=n);const o={type:"setextHeading",start:{...t[r][1].start},end:{...t[t.length-1][1].end}};return t[i][1].type="setextHeadingText",s?(t.splice(i,0,["enter",o,e]),t.splice(s+1,0,["exit",t[r][1],e]),t[r][1].end={...t[s][1].end}):t[r][1]=o,t.push(["exit",o,e]),t}function N8(t,e,n){const r=this;let i;return s;function s(d){let f=r.events.length,p;for(;f--;)if(r.events[f][1].type!=="lineEnding"&&r.events[f][1].type!=="linePrefix"&&r.events[f][1].type!=="content"){p=r.events[f][1].type==="paragraph";break}return!r.parser.lazy[r.now().line]&&(r.interrupt||p)?(t.enter("setextHeadingLine"),i=d,o(d)):n(d)}function o(d){return t.enter("setextHeadingLineSequence"),l(d)}function l(d){return d===i?(t.consume(d),l):(t.exit("setextHeadingLineSequence"),St(d)?Nt(t,c,"lineSuffix")(d):c(d))}function c(d){return d===null||et(d)?(t.exit("setextHeadingLine"),e(d)):n(d)}}const R8={tokenize:I8};function I8(t){const e=this,n=t.attempt(Dc,r,t.attempt(this.parser.constructs.flowInitial,i,Nt(t,t.attempt(this.parser.constructs.flow,i,t.attempt(P5,i)),"linePrefix")));return n;function r(s){if(s===null){t.consume(s);return}return t.enter("lineEndingBlank"),t.consume(s),t.exit("lineEndingBlank"),e.currentConstruct=void 0,n}function i(s){if(s===null){t.consume(s);return}return t.enter("lineEnding"),t.consume(s),t.exit("lineEnding"),e.currentConstruct=void 0,n}}const O8={resolveAll:bk()},M8=gk("string"),D8=gk("text");function gk(t){return{resolveAll:bk(t==="text"?L8:void 0),tokenize:e};function e(n){const r=this,i=this.parser.constructs[t],s=n.attempt(i,o,l);return o;function o(f){return d(f)?s(f):l(f)}function l(f){if(f===null){n.consume(f);return}return n.enter("data"),n.consume(f),c}function c(f){return d(f)?(n.exit("data"),s(f)):(n.consume(f),c)}function d(f){if(f===null)return!0;const p=i[f];let m=-1;if(p)for(;++m-1){const l=o[0];typeof l=="string"?o[0]=l.slice(r):o.shift()}s>0&&o.push(t[i].slice(0,s))}return o}function Y8(t,e){let n=-1;const r=[];let i;for(;++n0){const an=Qe.tokenStack[Qe.tokenStack.length-1];(an[1]||Ow).call(Qe,void 0,an[0])}for(ye.position={start:Qs(ce.length>0?ce[0][1].start:{line:1,column:1,offset:0}),end:Qs(ce.length>0?ce[ce.length-2][1].end:{line:1,column:1,offset:0})},rt=-1;++rt0&&(r.className=["language-"+i[0]]);let s={type:"element",tagName:"code",properties:r,children:[{type:"text",value:n}]};return e.meta&&(s.data={meta:e.meta}),t.patch(e,s),s=t.applyData(e,s),s={type:"element",tagName:"pre",properties:{},children:[s]},t.patch(e,s),s}function l9(t,e){const n={type:"element",tagName:"del",properties:{},children:t.all(e)};return t.patch(e,n),t.applyData(e,n)}function u9(t,e){const n={type:"element",tagName:"em",properties:{},children:t.all(e)};return t.patch(e,n),t.applyData(e,n)}function c9(t,e){const n=typeof t.options.clobberPrefix=="string"?t.options.clobberPrefix:"user-content-",r=String(e.identifier).toUpperCase(),i=Dl(r.toLowerCase()),s=t.footnoteOrder.indexOf(r);let o,l=t.footnoteCounts.get(r);l===void 0?(l=0,t.footnoteOrder.push(r),o=t.footnoteOrder.length):o=s+1,l+=1,t.footnoteCounts.set(r,l);const c={type:"element",tagName:"a",properties:{href:"#"+n+"fn-"+i,id:n+"fnref-"+i+(l>1?"-"+l:""),dataFootnoteRef:!0,ariaDescribedBy:["footnote-label"]},children:[{type:"text",value:String(o)}]};t.patch(e,c);const d={type:"element",tagName:"sup",properties:{},children:[c]};return t.patch(e,d),t.applyData(e,d)}function d9(t,e){const n={type:"element",tagName:"h"+e.depth,properties:{},children:t.all(e)};return t.patch(e,n),t.applyData(e,n)}function f9(t,e){if(t.options.allowDangerousHtml){const n={type:"raw",value:e.value};return t.patch(e,n),t.applyData(e,n)}}function xk(t,e){const n=e.referenceType;let r="]";if(n==="collapsed"?r+="[]":n==="full"&&(r+="["+(e.label||e.identifier)+"]"),e.type==="imageReference")return[{type:"text",value:"!["+e.alt+r}];const i=t.all(e),s=i[0];s&&s.type==="text"?s.value="["+s.value:i.unshift({type:"text",value:"["});const o=i[i.length-1];return o&&o.type==="text"?o.value+=r:i.push({type:"text",value:r}),i}function h9(t,e){const n=String(e.identifier).toUpperCase(),r=t.definitionById.get(n);if(!r)return xk(t,e);const i={src:Dl(r.url||""),alt:e.alt};r.title!==null&&r.title!==void 0&&(i.title=r.title);const s={type:"element",tagName:"img",properties:i,children:[]};return t.patch(e,s),t.applyData(e,s)}function p9(t,e){const n={src:Dl(e.url)};e.alt!==null&&e.alt!==void 0&&(n.alt=e.alt),e.title!==null&&e.title!==void 0&&(n.title=e.title);const r={type:"element",tagName:"img",properties:n,children:[]};return t.patch(e,r),t.applyData(e,r)}function m9(t,e){const n={type:"text",value:e.value.replace(/\r?\n|\r/g," ")};t.patch(e,n);const r={type:"element",tagName:"code",properties:{},children:[n]};return t.patch(e,r),t.applyData(e,r)}function g9(t,e){const n=String(e.identifier).toUpperCase(),r=t.definitionById.get(n);if(!r)return xk(t,e);const i={href:Dl(r.url||"")};r.title!==null&&r.title!==void 0&&(i.title=r.title);const s={type:"element",tagName:"a",properties:i,children:t.all(e)};return t.patch(e,s),t.applyData(e,s)}function b9(t,e){const n={href:Dl(e.url)};e.title!==null&&e.title!==void 0&&(n.title=e.title);const r={type:"element",tagName:"a",properties:n,children:t.all(e)};return t.patch(e,r),t.applyData(e,r)}function E9(t,e,n){const r=t.all(e),i=n?y9(n):vk(e),s={},o=[];if(typeof e.checked=="boolean"){const f=r[0];let p;f&&f.type==="element"&&f.tagName==="p"?p=f:(p={type:"element",tagName:"p",properties:{},children:[]},r.unshift(p)),p.children.length>0&&p.children.unshift({type:"text",value:" "}),p.children.unshift({type:"element",tagName:"input",properties:{type:"checkbox",checked:e.checked,disabled:!0},children:[]}),s.className=["task-list-item"]}let l=-1;for(;++l1}function x9(t,e){const n={},r=t.all(e);let i=-1;for(typeof e.start=="number"&&e.start!==1&&(n.start=e.start);++i0){const o={type:"element",tagName:"tbody",properties:{},children:t.wrap(n,!0)},l=Ji(e.children[1]),c=op(e.children[e.children.length-1]);l&&c&&(o.position={start:l,end:c}),i.push(o)}const s={type:"element",tagName:"table",properties:{},children:t.wrap(i,!0)};return t.patch(e,s),t.applyData(e,s)}function _9(t,e,n){const r=n?n.children:void 0,s=(r?r.indexOf(e):1)===0?"th":"td",o=n&&n.type==="table"?n.align:void 0,l=o?o.length:e.children.length;let c=-1;const d=[];for(;++c0,!0),r[0]),i=r.index+r[0].length,r=n.exec(e);return s.push(Lw(e.slice(i),i>0,!1)),s.join("")}function Lw(t,e,n){let r=0,i=t.length;if(e){let s=t.codePointAt(r);for(;s===Mw||s===Dw;)r++,s=t.codePointAt(r)}if(n){let s=t.codePointAt(i-1);for(;s===Mw||s===Dw;)i--,s=t.codePointAt(i-1)}return i>r?t.slice(r,i):""}function k9(t,e){const n={type:"text",value:A9(String(e.value))};return t.patch(e,n),t.applyData(e,n)}function N9(t,e){const n={type:"element",tagName:"hr",properties:{},children:[]};return t.patch(e,n),t.applyData(e,n)}const R9={blockquote:s9,break:o9,code:a9,delete:l9,emphasis:u9,footnoteReference:c9,heading:d9,html:f9,imageReference:h9,image:p9,inlineCode:m9,linkReference:g9,link:b9,listItem:E9,list:x9,paragraph:v9,root:w9,strong:T9,table:S9,tableCell:C9,tableRow:_9,text:k9,thematicBreak:N9,toml:lf,yaml:lf,definition:lf,footnoteDefinition:lf};function lf(){}const wk=-1,up=0,Yu=1,ch=2,XE=3,QE=4,ZE=5,JE=6,Tk=7,Sk=8,Pw=typeof self=="object"?self:globalThis,I9=(t,e)=>{const n=(i,s)=>(t.set(s,i),i),r=i=>{if(t.has(i))return t.get(i);const[s,o]=e[i];switch(s){case up:case wk:return n(o,i);case Yu:{const l=n([],i);for(const c of o)l.push(r(c));return l}case ch:{const l=n({},i);for(const[c,d]of o)l[r(c)]=r(d);return l}case XE:return n(new Date(o),i);case QE:{const{source:l,flags:c}=o;return n(new RegExp(l,c),i)}case ZE:{const l=n(new Map,i);for(const[c,d]of o)l.set(r(c),r(d));return l}case JE:{const l=n(new Set,i);for(const c of o)l.add(r(c));return l}case Tk:{const{name:l,message:c}=o;return n(new Pw[l](c),i)}case Sk:return n(BigInt(o),i);case"BigInt":return n(Object(BigInt(o)),i);case"ArrayBuffer":return n(new Uint8Array(o).buffer,o);case"DataView":{const{buffer:l}=new Uint8Array(o);return n(new DataView(l),o)}}return n(new Pw[s](o),i)};return r},Fw=t=>I9(new Map,t)(0),ja="",{toString:O9}={},{keys:M9}=Object,Iu=t=>{const e=typeof t;if(e!=="object"||!t)return[up,e];const n=O9.call(t).slice(8,-1);switch(n){case"Array":return[Yu,ja];case"Object":return[ch,ja];case"Date":return[XE,ja];case"RegExp":return[QE,ja];case"Map":return[ZE,ja];case"Set":return[JE,ja];case"DataView":return[Yu,n]}return n.includes("Array")?[Yu,n]:n.includes("Error")?[Tk,n]:[ch,n]},uf=([t,e])=>t===up&&(e==="function"||e==="symbol"),D9=(t,e,n,r)=>{const i=(o,l)=>{const c=r.push(o)-1;return n.set(l,c),c},s=o=>{if(n.has(o))return n.get(o);let[l,c]=Iu(o);switch(l){case up:{let f=o;switch(c){case"bigint":l=Sk,f=o.toString();break;case"function":case"symbol":if(t)throw new TypeError("unable to serialize "+c);f=null;break;case"undefined":return i([wk],o)}return i([l,f],o)}case Yu:{if(c){let m=o;return c==="DataView"?m=new Uint8Array(o.buffer):c==="ArrayBuffer"&&(m=new Uint8Array(o)),i([c,[...m]],o)}const f=[],p=i([l,f],o);for(const m of o)f.push(s(m));return p}case ch:{if(c)switch(c){case"BigInt":return i([c,o.toString()],o);case"Boolean":case"Number":case"String":return i([c,o.valueOf()],o)}if(e&&"toJSON"in o)return s(o.toJSON());const f=[],p=i([l,f],o);for(const m of M9(o))(t||!uf(Iu(o[m])))&&f.push([s(m),s(o[m])]);return p}case XE:return i([l,o.toISOString()],o);case QE:{const{source:f,flags:p}=o;return i([l,{source:f,flags:p}],o)}case ZE:{const f=[],p=i([l,f],o);for(const[m,g]of o)(t||!(uf(Iu(m))||uf(Iu(g))))&&f.push([s(m),s(g)]);return p}case JE:{const f=[],p=i([l,f],o);for(const m of o)(t||!uf(Iu(m)))&&f.push(s(m));return p}}const{message:d}=o;return i([l,{name:c,message:d}],o)};return s},Bw=(t,{json:e,lossy:n}={})=>{const r=[];return D9(!(e||n),!!e,new Map,r)(t),r},El=typeof structuredClone=="function"?(t,e)=>e&&("json"in e||"lossy"in e)?Fw(Bw(t,e)):structuredClone(t):(t,e)=>Fw(Bw(t,e));function L9(t,e){const n=[{type:"text",value:"↩"}];return e>1&&n.push({type:"element",tagName:"sup",properties:{},children:[{type:"text",value:String(e)}]}),n}function P9(t,e){return"Back to reference "+(t+1)+(e>1?"-"+e:"")}function F9(t){const e=typeof t.options.clobberPrefix=="string"?t.options.clobberPrefix:"user-content-",n=t.options.footnoteBackContent||L9,r=t.options.footnoteBackLabel||P9,i=t.options.footnoteLabel||"Footnotes",s=t.options.footnoteLabelTagName||"h2",o=t.options.footnoteLabelProperties||{className:["sr-only"]},l=[];let c=-1;for(;++c0&&x.push({type:"text",value:" "});let A=typeof n=="string"?n:n(c,g);typeof A=="string"&&(A={type:"text",value:A}),x.push({type:"element",tagName:"a",properties:{href:"#"+e+"fnref-"+m+(g>1?"-"+g:""),dataFootnoteBackref:"",ariaLabel:typeof r=="string"?r:r(c,g),className:["data-footnote-backref"]},children:Array.isArray(A)?A:[A]})}const S=f[f.length-1];if(S&&S.type==="element"&&S.tagName==="p"){const A=S.children[S.children.length-1];A&&A.type==="text"?A.value+=" ":S.children.push({type:"text",value:" "}),S.children.push(...x)}else f.push(...x);const C={type:"element",tagName:"li",properties:{id:e+"fn-"+m},children:t.wrap(f,!0)};t.patch(d,C),l.push(C)}if(l.length!==0)return{type:"element",tagName:"section",properties:{dataFootnotes:!0,className:["footnotes"]},children:[{type:"element",tagName:s,properties:{...El(o),id:"footnote-label"},children:[{type:"text",value:i}]},{type:"text",value:` `},{type:"element",tagName:"ol",properties:{},children:t.wrap(l,!0)},{type:"text",value:` `}]}}const Lc=(function(t){if(t==null)return z9;if(typeof t=="function")return cp(t);if(typeof t=="object")return Array.isArray(t)?B9(t):U9(t);if(typeof t=="string")return H9(t);throw new Error("Expected function, string, or object as test")});function B9(t){const e=[];let n=-1;for(;++n":""))+")"})}return m;function m(){let g=_k,x,v,S;if((!e||s(c,d,f[f.length-1]||void 0))&&(g=V9(n(c,f)),g[0]===Q0))return g;if("children"in c&&c.children){const C=c;if(C.children&&g[0]!==W9)for(v=(r?C.children.length:-1)+o,S=f.concat(C);v>-1&&v0&&n.push({type:"text",value:` `}),n}function Uw(t){let e=0,n=t.charCodeAt(e);for(;n===9||n===32;)e++,n=t.charCodeAt(e);return t.slice(e)}function Hw(t,e){const n=K9(t,e),r=n.one(t,void 0),i=F9(n),s=Array.isArray(r)?{type:"root",children:r}:r||{type:"root",children:[]};return i&&s.children.push({type:"text",value:` `},i),s}function Z9(t,e){return t&&"run"in t?async function(n,r){const i=Hw(n,{file:r,...e});await t.run(i,r)}:function(n,r){return Hw(n,{file:r,...t||e})}}function zw(t){if(t)throw t}var zg,jw;function J9(){if(jw)return zg;jw=1;var t=Object.prototype.hasOwnProperty,e=Object.prototype.toString,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=function(d){return typeof Array.isArray=="function"?Array.isArray(d):e.call(d)==="[object Array]"},s=function(d){if(!d||e.call(d)!=="[object Object]")return!1;var f=t.call(d,"constructor"),p=d.constructor&&d.constructor.prototype&&t.call(d.constructor.prototype,"isPrototypeOf");if(d.constructor&&!f&&!p)return!1;var m;for(m in d);return typeof m>"u"||t.call(d,m)},o=function(d,f){n&&f.name==="__proto__"?n(d,f.name,{enumerable:!0,configurable:!0,value:f.newValue,writable:!0}):d[f.name]=f.newValue},l=function(d,f){if(f==="__proto__")if(t.call(d,f)){if(r)return r(d,f).value}else return;return d[f]};return zg=function c(){var d,f,p,m,g,x,v=arguments[0],S=1,C=arguments.length,A=!1;for(typeof v=="boolean"&&(A=v,v=arguments[1]||{},S=2),(v==null||typeof v!="object"&&typeof v!="function")&&(v={});So.length;let c;l&&o.push(i);try{c=t.apply(this,o)}catch(d){const f=d;if(l&&n)throw f;return i(f)}l||(c&&c.then&&typeof c.then=="function"?c.then(s,i):c instanceof Error?i(c):s(c))}function i(o,...l){n||(n=!0,e(o,...l))}function s(o){i(null,o)}}const Ui={basename:rU,dirname:iU,extname:sU,join:oU,sep:"/"};function rU(t,e){if(e!==void 0&&typeof e!="string")throw new TypeError('"ext" argument must be a string');Fc(t);let n=0,r=-1,i=t.length,s;if(e===void 0||e.length===0||e.length>t.length){for(;i--;)if(t.codePointAt(i)===47){if(s){n=i+1;break}}else r<0&&(s=!0,r=i+1);return r<0?"":t.slice(n,r)}if(e===t)return"";let o=-1,l=e.length-1;for(;i--;)if(t.codePointAt(i)===47){if(s){n=i+1;break}}else o<0&&(s=!0,o=i+1),l>-1&&(t.codePointAt(i)===e.codePointAt(l--)?l<0&&(r=i):(l=-1,r=o));return n===r?r=o:r<0&&(r=t.length),t.slice(n,r)}function iU(t){if(Fc(t),t.length===0)return".";let e=-1,n=t.length,r;for(;--n;)if(t.codePointAt(n)===47){if(r){e=n;break}}else r||(r=!0);return e<0?t.codePointAt(0)===47?"/":".":e===1&&t.codePointAt(0)===47?"//":t.slice(0,e)}function sU(t){Fc(t);let e=t.length,n=-1,r=0,i=-1,s=0,o;for(;e--;){const l=t.codePointAt(e);if(l===47){if(o){r=e+1;break}continue}n<0&&(o=!0,n=e+1),l===46?i<0?i=e:s!==1&&(s=1):i>-1&&(s=-1)}return i<0||n<0||s===0||s===1&&i===n-1&&i===r+1?"":t.slice(i,n)}function oU(...t){let e=-1,n;for(;++e0&&t.codePointAt(t.length-1)===47&&(n+="/"),e?"/"+n:n}function lU(t,e){let n="",r=0,i=-1,s=0,o=-1,l,c;for(;++o<=t.length;){if(o2){if(c=n.lastIndexOf("/"),c!==n.length-1){c<0?(n="",r=0):(n=n.slice(0,c),r=n.length-1-n.lastIndexOf("/")),i=o,s=0;continue}}else if(n.length>0){n="",r=0,i=o,s=0;continue}}e&&(n=n.length>0?n+"/..":"..",r=2)}else n.length>0?n+="/"+t.slice(i+1,o):n=t.slice(i+1,o),r=o-i-1;i=o,s=0}else l===46&&s>-1?s++:s=-1}return n}function Fc(t){if(typeof t!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(t))}const uU={cwd:cU};function cU(){return"/"}function eb(t){return!!(t!==null&&typeof t=="object"&&"href"in t&&t.href&&"protocol"in t&&t.protocol&&t.auth===void 0)}function dU(t){if(typeof t=="string")t=new URL(t);else if(!eb(t)){const e=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+t+"`");throw e.code="ERR_INVALID_ARG_TYPE",e}if(t.protocol!=="file:"){const e=new TypeError("The URL must be of scheme file");throw e.code="ERR_INVALID_URL_SCHEME",e}return fU(t)}function fU(t){if(t.hostname!==""){const r=new TypeError('File URL host must be "localhost" or empty on darwin');throw r.code="ERR_INVALID_FILE_URL_HOST",r}const e=t.pathname;let n=-1;for(;++n0){let[g,...x]=f;const v=r[m][1];J0(v)&&J0(g)&&(g=jg(!0,v,g)),r[m]=[d,g,...x]}}}}const gU=new e1().freeze();function Gg(t,e){if(typeof e!="function")throw new TypeError("Cannot `"+t+"` without `parser`")}function Kg(t,e){if(typeof e!="function")throw new TypeError("Cannot `"+t+"` without `compiler`")}function Yg(t,e){if(e)throw new Error("Cannot call `"+t+"` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.")}function Ww(t){if(!J0(t)||typeof t.type!="string")throw new TypeError("Expected node, got `"+t+"`")}function Vw(t,e,n){if(!n)throw new Error("`"+t+"` finished async. Use `"+e+"` instead")}function cf(t){return bU(t)?t:new Ak(t)}function bU(t){return!!(t&&typeof t=="object"&&"message"in t&&"messages"in t)}function EU(t){return typeof t=="string"||yU(t)}function yU(t){return!!(t&&typeof t=="object"&&"byteLength"in t&&"byteOffset"in t)}const xU="https://github.com/remarkjs/react-markdown/blob/main/changelog.md",Gw=[],Kw={allowDangerousHtml:!0},vU=/^(https?|ircs?|mailto|xmpp)$/i,wU=[{from:"astPlugins",id:"remove-buggy-html-in-markdown-parser"},{from:"allowDangerousHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"allowNode",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowElement"},{from:"allowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowedElements"},{from:"disallowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"disallowedElements"},{from:"escapeHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"includeElementIndex",id:"#remove-includeelementindex"},{from:"includeNodeIndex",id:"change-includenodeindex-to-includeelementindex"},{from:"linkTarget",id:"remove-linktarget"},{from:"plugins",id:"change-plugins-to-remarkplugins",to:"remarkPlugins"},{from:"rawSourcePos",id:"#remove-rawsourcepos"},{from:"renderers",id:"change-renderers-to-components",to:"components"},{from:"source",id:"change-source-to-children",to:"children"},{from:"sourcePos",id:"#remove-sourcepos"},{from:"transformImageUri",id:"#add-urltransform",to:"urlTransform"},{from:"transformLinkUri",id:"#add-urltransform",to:"urlTransform"}];function TU(t){const e=SU(t),n=_U(t);return CU(e.runSync(e.parse(n),n),t)}function SU(t){const e=t.rehypePlugins||Gw,n=t.remarkPlugins||Gw,r=t.remarkRehypeOptions?{...t.remarkRehypeOptions,...Kw}:Kw;return gU().use(i9).use(n).use(Z9,r).use(e)}function _U(t){const e=t.children||"",n=new Ak;return typeof e=="string"&&(n.value=e),n}function CU(t,e){const n=e.allowedElements,r=e.allowElement,i=e.components,s=e.disallowedElements,o=e.skipHtml,l=e.unwrapDisallowed,c=e.urlTransform||AU;for(const f of wU)Object.hasOwn(e,f.from)&&(""+f.from+(f.to?"use `"+f.to+"` instead":"remove it")+xU+f.id,void 0);return e.className&&(t={type:"element",tagName:"div",properties:{className:e.className},children:t.type==="root"?t.children:[t]}),Pc(t,d),zB(t,{Fragment:w.Fragment,components:i,ignoreInvalidStyle:!0,jsx:w.jsx,jsxs:w.jsxs,passKeys:!0,passNode:!0});function d(f,p,m){if(f.type==="raw"&&m&&typeof p=="number")return o?m.children.splice(p,1):m.children[p]={type:"text",value:f.value},p;if(f.type==="element"){let g;for(g in Bg)if(Object.hasOwn(Bg,g)&&Object.hasOwn(f.properties,g)){const x=f.properties[g],v=Bg[g];(v===null||v.includes(f.tagName))&&(f.properties[g]=c(String(x||""),g,f))}}if(f.type==="element"){let g=n?!n.includes(f.tagName):s?s.includes(f.tagName):!1;if(!g&&r&&typeof p=="number"&&(g=!r(f,p,m)),g&&m&&typeof p=="number")return l&&f.children?m.children.splice(p,1,...f.children):m.children.splice(p,1),p}}}function AU(t){const e=t.indexOf(":"),n=t.indexOf("?"),r=t.indexOf("#"),i=t.indexOf("/");return e===-1||i!==-1&&e>i||n!==-1&&e>n||r!==-1&&e>r||vU.test(t.slice(0,e))?t:""}function Yw(t,e){const n=String(t);if(typeof e!="string")throw new TypeError("Expected character");let r=0,i=n.indexOf(e);for(;i!==-1;)r++,i=n.indexOf(e,i+e.length);return r}function kU(t){if(typeof t!="string")throw new TypeError("Expected a string");return t.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&").replace(/-/g,"\\x2d")}function NU(t,e,n){const i=Lc((n||{}).ignore||[]),s=RU(e);let o=-1;for(;++o0?{type:"text",value:I}:void 0),I===!1?m.lastIndex=M+1:(x!==M&&A.push({type:"text",value:d.value.slice(x,M)}),Array.isArray(I)?A.push(...I):I&&A.push(I),x=M+k[0].length,C=!0),!m.global)break;k=m.exec(d.value)}return C?(x?\]}]+$/.exec(t);if(!e)return[t,void 0];t=t.slice(0,e.index);let n=e[0],r=n.indexOf(")");const i=Yw(t,"(");let s=Yw(t,")");for(;r!==-1&&i>s;)t+=n.slice(0,r+1),n=n.slice(r+1),r=n.indexOf(")"),s++;return[t,n]}function kk(t,e){const n=t.input.charCodeAt(t.index-1);return(t.index===0||Xo(n)||ap(n))&&(!e||n!==47)}Nk.peek=JU;function VU(){this.buffer()}function GU(t){this.enter({type:"footnoteReference",identifier:"",label:""},t)}function KU(){this.buffer()}function YU(t){this.enter({type:"footnoteDefinition",identifier:"",label:"",children:[]},t)}function qU(t){const e=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=vi(this.sliceSerialize(t)).toLowerCase(),n.label=e}function XU(t){this.exit(t)}function QU(t){const e=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=vi(this.sliceSerialize(t)).toLowerCase(),n.label=e}function ZU(t){this.exit(t)}function JU(){return"["}function Nk(t,e,n,r){const i=n.createTracker(r);let s=i.move("[^");const o=n.enter("footnoteReference"),l=n.enter("reference");return s+=i.move(n.safe(n.associationId(t),{after:"]",before:s})),l(),o(),s+=i.move("]"),s}function eH(){return{enter:{gfmFootnoteCallString:VU,gfmFootnoteCall:GU,gfmFootnoteDefinitionLabelString:KU,gfmFootnoteDefinition:YU},exit:{gfmFootnoteCallString:qU,gfmFootnoteCall:XU,gfmFootnoteDefinitionLabelString:QU,gfmFootnoteDefinition:ZU}}}function tH(t){let e=!1;return t&&t.firstLineBlank&&(e=!0),{handlers:{footnoteDefinition:n,footnoteReference:Nk},unsafe:[{character:"[",inConstruct:["label","phrasing","reference"]}]};function n(r,i,s,o){const l=s.createTracker(o);let c=l.move("[^");const d=s.enter("footnoteDefinition"),f=s.enter("label");return c+=l.move(s.safe(s.associationId(r),{before:c,after:"]"})),f(),c+=l.move("]:"),r.children&&r.children.length>0&&(l.shift(4),c+=l.move((e?` `:" ")+s.indentLines(s.containerFlow(r,l.current()),e?Rk:nH))),d(),c}}function nH(t,e,n){return e===0?t:Rk(t,e,n)}function Rk(t,e,n){return(n?"":" ")+t}const rH=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];Ik.peek=lH;function iH(){return{canContainEols:["delete"],enter:{strikethrough:oH},exit:{strikethrough:aH}}}function sH(){return{unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:rH}],handlers:{delete:Ik}}}function oH(t){this.enter({type:"delete",children:[]},t)}function aH(t){this.exit(t)}function Ik(t,e,n,r){const i=n.createTracker(r),s=n.enter("strikethrough");let o=i.move("~~");return o+=n.containerPhrasing(t,{...i.current(),before:o,after:"~"}),o+=i.move("~~"),s(),o}function lH(){return"~"}function uH(t){return t.length}function cH(t,e){const n=e||{},r=(n.align||[]).concat(),i=n.stringLength||uH,s=[],o=[],l=[],c=[];let d=0,f=-1;for(;++fd&&(d=t[f].length);++Cc[C])&&(c[C]=k)}v.push(A)}o[f]=v,l[f]=S}let p=-1;if(typeof r=="object"&&"length"in r)for(;++pc[p]&&(c[p]=A),g[p]=A),m[p]=k}o.splice(1,0,m),l.splice(1,0,g),f=-1;const x=[];for(;++f "),s.shift(2);const o=n.indentLines(n.containerFlow(t,s.current()),hH);return i(),o}function hH(t,e,n){return">"+(n?"":" ")+t}function pH(t,e){return Qw(t,e.inConstruct,!0)&&!Qw(t,e.notInConstruct,!1)}function Qw(t,e,n){if(typeof e=="string"&&(e=[e]),!e||e.length===0)return n;let r=-1;for(;++ro&&(o=s):s=1,i=r+e.length,r=n.indexOf(e,i);return o}function gH(t,e){return!!(e.options.fences===!1&&t.value&&!t.lang&&/[^ \r\n]/.test(t.value)&&!/^[\t ]*(?:[\r\n]|$)|(?:^|[\r\n])[\t ]*$/.test(t.value))}function bH(t){const e=t.options.fence||"`";if(e!=="`"&&e!=="~")throw new Error("Cannot serialize code with `"+e+"` for `options.fence`, expected `` ` `` or `~`");return e}function EH(t,e,n,r){const i=bH(n),s=t.value||"",o=i==="`"?"GraveAccent":"Tilde";if(gH(t,n)){const p=n.enter("codeIndented"),m=n.indentLines(s,yH);return p(),m}const l=n.createTracker(r),c=i.repeat(Math.max(mH(s,i)+1,3)),d=n.enter("codeFenced");let f=l.move(c);if(t.lang){const p=n.enter(`codeFencedLang${o}`);f+=l.move(n.safe(t.lang,{before:f,after:" ",encode:["`"],...l.current()})),p()}if(t.lang&&t.meta){const p=n.enter(`codeFencedMeta${o}`);f+=l.move(" "),f+=l.move(n.safe(t.meta,{before:f,after:` `,encode:["`"],...l.current()})),p()}return f+=l.move(` `),s&&(f+=l.move(s+` `)),f+=l.move(c),d(),f}function yH(t,e,n){return(n?"":" ")+t}function t1(t){const e=t.options.quote||'"';if(e!=='"'&&e!=="'")throw new Error("Cannot serialize title with `"+e+"` for `options.quote`, expected `\"`, or `'`");return e}function xH(t,e,n,r){const i=t1(n),s=i==='"'?"Quote":"Apostrophe",o=n.enter("definition");let l=n.enter("label");const c=n.createTracker(r);let d=c.move("[");return d+=c.move(n.safe(n.associationId(t),{before:d,after:"]",...c.current()})),d+=c.move("]: "),l(),!t.url||/[\0- \u007F]/.test(t.url)?(l=n.enter("destinationLiteral"),d+=c.move("<"),d+=c.move(n.safe(t.url,{before:d,after:">",...c.current()})),d+=c.move(">")):(l=n.enter("destinationRaw"),d+=c.move(n.safe(t.url,{before:d,after:t.title?" ":` `,...c.current()}))),l(),t.title&&(l=n.enter(`title${s}`),d+=c.move(" "+i),d+=c.move(n.safe(t.title,{before:d,after:i,...c.current()})),d+=c.move(i),l()),o(),d}function vH(t){const e=t.options.emphasis||"*";if(e!=="*"&&e!=="_")throw new Error("Cannot serialize emphasis with `"+e+"` for `options.emphasis`, expected `*`, or `_`");return e}function pc(t){return"&#x"+t.toString(16).toUpperCase()+";"}function dh(t,e,n){const r=bl(t),i=bl(e);return r===void 0?i===void 0?n==="_"?{inside:!0,outside:!0}:{inside:!1,outside:!1}:i===1?{inside:!0,outside:!0}:{inside:!1,outside:!0}:r===1?i===void 0?{inside:!1,outside:!1}:i===1?{inside:!0,outside:!0}:{inside:!1,outside:!1}:i===void 0?{inside:!1,outside:!1}:i===1?{inside:!0,outside:!1}:{inside:!1,outside:!1}}Mk.peek=wH;function Mk(t,e,n,r){const i=vH(n),s=n.enter("emphasis"),o=n.createTracker(r),l=o.move(i);let c=o.move(n.containerPhrasing(t,{after:i,before:l,...o.current()}));const d=c.charCodeAt(0),f=dh(r.before.charCodeAt(r.before.length-1),d,i);f.inside&&(c=pc(d)+c.slice(1));const p=c.charCodeAt(c.length-1),m=dh(r.after.charCodeAt(0),p,i);m.inside&&(c=c.slice(0,-1)+pc(p));const g=o.move(i);return s(),n.attentionEncodeSurroundingInfo={after:m.outside,before:f.outside},l+c+g}function wH(t,e,n){return n.options.emphasis||"*"}function TH(t,e){let n=!1;return Pc(t,function(r){if("value"in r&&/\r?\n|\r/.test(r.value)||r.type==="break")return n=!0,Q0}),!!((!t.depth||t.depth<3)&&KE(t)&&(e.options.setext||n))}function SH(t,e,n,r){const i=Math.max(Math.min(6,t.depth||1),1),s=n.createTracker(r);if(TH(t,n)){const f=n.enter("headingSetext"),p=n.enter("phrasing"),m=n.containerPhrasing(t,{...s.current(),before:` `,after:` `});return p(),f(),m+` `+(i===1?"=":"-").repeat(m.length-(Math.max(m.lastIndexOf("\r"),m.lastIndexOf(` `))+1))}const o="#".repeat(i),l=n.enter("headingAtx"),c=n.enter("phrasing");s.move(o+" ");let d=n.containerPhrasing(t,{before:"# ",after:` `,...s.current()});return/^[\t ]/.test(d)&&(d=pc(d.charCodeAt(0))+d.slice(1)),d=d?o+" "+d:o,n.options.closeAtx&&(d+=" "+o),c(),l(),d}Dk.peek=_H;function Dk(t){return t.value||""}function _H(){return"<"}Lk.peek=CH;function Lk(t,e,n,r){const i=t1(n),s=i==='"'?"Quote":"Apostrophe",o=n.enter("image");let l=n.enter("label");const c=n.createTracker(r);let d=c.move("![");return d+=c.move(n.safe(t.alt,{before:d,after:"]",...c.current()})),d+=c.move("]("),l(),!t.url&&t.title||/[\0- \u007F]/.test(t.url)?(l=n.enter("destinationLiteral"),d+=c.move("<"),d+=c.move(n.safe(t.url,{before:d,after:">",...c.current()})),d+=c.move(">")):(l=n.enter("destinationRaw"),d+=c.move(n.safe(t.url,{before:d,after:t.title?" ":")",...c.current()}))),l(),t.title&&(l=n.enter(`title${s}`),d+=c.move(" "+i),d+=c.move(n.safe(t.title,{before:d,after:i,...c.current()})),d+=c.move(i),l()),d+=c.move(")"),o(),d}function CH(){return"!"}Pk.peek=AH;function Pk(t,e,n,r){const i=t.referenceType,s=n.enter("imageReference");let o=n.enter("label");const l=n.createTracker(r);let c=l.move("![");const d=n.safe(t.alt,{before:c,after:"]",...l.current()});c+=l.move(d+"]["),o();const f=n.stack;n.stack=[],o=n.enter("reference");const p=n.safe(n.associationId(t),{before:c,after:"]",...l.current()});return o(),n.stack=f,s(),i==="full"||!d||d!==p?c+=l.move(p+"]"):i==="shortcut"?c=c.slice(0,-1):c+=l.move("]"),c}function AH(){return"!"}Fk.peek=kH;function Fk(t,e,n){let r=t.value||"",i="`",s=-1;for(;new RegExp("(^|[^`])"+i+"([^`]|$)").test(r);)i+="`";for(/[^ \r\n]/.test(r)&&(/^[ \r\n]/.test(r)&&/[ \r\n]$/.test(r)||/^`|`$/.test(r))&&(r=" "+r+" ");++s\u007F]/.test(t.url))}Uk.peek=NH;function Uk(t,e,n,r){const i=t1(n),s=i==='"'?"Quote":"Apostrophe",o=n.createTracker(r);let l,c;if(Bk(t,n)){const f=n.stack;n.stack=[],l=n.enter("autolink");let p=o.move("<");return p+=o.move(n.containerPhrasing(t,{before:p,after:">",...o.current()})),p+=o.move(">"),l(),n.stack=f,p}l=n.enter("link"),c=n.enter("label");let d=o.move("[");return d+=o.move(n.containerPhrasing(t,{before:d,after:"](",...o.current()})),d+=o.move("]("),c(),!t.url&&t.title||/[\0- \u007F]/.test(t.url)?(c=n.enter("destinationLiteral"),d+=o.move("<"),d+=o.move(n.safe(t.url,{before:d,after:">",...o.current()})),d+=o.move(">")):(c=n.enter("destinationRaw"),d+=o.move(n.safe(t.url,{before:d,after:t.title?" ":")",...o.current()}))),c(),t.title&&(c=n.enter(`title${s}`),d+=o.move(" "+i),d+=o.move(n.safe(t.title,{before:d,after:i,...o.current()})),d+=o.move(i),c()),d+=o.move(")"),l(),d}function NH(t,e,n){return Bk(t,n)?"<":"["}Hk.peek=RH;function Hk(t,e,n,r){const i=t.referenceType,s=n.enter("linkReference");let o=n.enter("label");const l=n.createTracker(r);let c=l.move("[");const d=n.containerPhrasing(t,{before:c,after:"]",...l.current()});c+=l.move(d+"]["),o();const f=n.stack;n.stack=[],o=n.enter("reference");const p=n.safe(n.associationId(t),{before:c,after:"]",...l.current()});return o(),n.stack=f,s(),i==="full"||!d||d!==p?c+=l.move(p+"]"):i==="shortcut"?c=c.slice(0,-1):c+=l.move("]"),c}function RH(){return"["}function n1(t){const e=t.options.bullet||"*";if(e!=="*"&&e!=="+"&&e!=="-")throw new Error("Cannot serialize items with `"+e+"` for `options.bullet`, expected `*`, `+`, or `-`");return e}function IH(t){const e=n1(t),n=t.options.bulletOther;if(!n)return e==="*"?"-":"*";if(n!=="*"&&n!=="+"&&n!=="-")throw new Error("Cannot serialize items with `"+n+"` for `options.bulletOther`, expected `*`, `+`, or `-`");if(n===e)throw new Error("Expected `bullet` (`"+e+"`) and `bulletOther` (`"+n+"`) to be different");return n}function OH(t){const e=t.options.bulletOrdered||".";if(e!=="."&&e!==")")throw new Error("Cannot serialize items with `"+e+"` for `options.bulletOrdered`, expected `.` or `)`");return e}function zk(t){const e=t.options.rule||"*";if(e!=="*"&&e!=="-"&&e!=="_")throw new Error("Cannot serialize rules with `"+e+"` for `options.rule`, expected `*`, `-`, or `_`");return e}function MH(t,e,n,r){const i=n.enter("list"),s=n.bulletCurrent;let o=t.ordered?OH(n):n1(n);const l=t.ordered?o==="."?")":".":IH(n);let c=e&&n.bulletLastUsed?o===n.bulletLastUsed:!1;if(!t.ordered){const f=t.children?t.children[0]:void 0;if((o==="*"||o==="-")&&f&&(!f.children||!f.children[0])&&n.stack[n.stack.length-1]==="list"&&n.stack[n.stack.length-2]==="listItem"&&n.stack[n.stack.length-3]==="list"&&n.stack[n.stack.length-4]==="listItem"&&n.indexStack[n.indexStack.length-1]===0&&n.indexStack[n.indexStack.length-2]===0&&n.indexStack[n.indexStack.length-3]===0&&(c=!0),zk(n)===o&&f){let p=-1;for(;++p-1?e.start:1)+(n.options.incrementListMarker===!1?0:e.children.indexOf(t))+s);let o=s.length+1;(i==="tab"||i==="mixed"&&(e&&e.type==="list"&&e.spread||t.spread))&&(o=Math.ceil(o/4)*4);const l=n.createTracker(r);l.move(s+" ".repeat(o-s.length)),l.shift(o);const c=n.enter("listItem"),d=n.indentLines(n.containerFlow(t,l.current()),f);return c(),d;function f(p,m,g){return m?(g?"":" ".repeat(o))+p:(g?s:s+" ".repeat(o-s.length))+p}}function PH(t,e,n,r){const i=n.enter("paragraph"),s=n.enter("phrasing"),o=n.containerPhrasing(t,r);return s(),i(),o}const FH=Lc(["break","delete","emphasis","footnote","footnoteReference","image","imageReference","inlineCode","inlineMath","link","linkReference","mdxJsxTextElement","mdxTextExpression","strong","text","textDirective"]);function BH(t,e,n,r){return(t.children.some(function(o){return FH(o)})?n.containerPhrasing:n.containerFlow).call(n,t,r)}function UH(t){const e=t.options.strong||"*";if(e!=="*"&&e!=="_")throw new Error("Cannot serialize strong with `"+e+"` for `options.strong`, expected `*`, or `_`");return e}jk.peek=HH;function jk(t,e,n,r){const i=UH(n),s=n.enter("strong"),o=n.createTracker(r),l=o.move(i+i);let c=o.move(n.containerPhrasing(t,{after:i,before:l,...o.current()}));const d=c.charCodeAt(0),f=dh(r.before.charCodeAt(r.before.length-1),d,i);f.inside&&(c=pc(d)+c.slice(1));const p=c.charCodeAt(c.length-1),m=dh(r.after.charCodeAt(0),p,i);m.inside&&(c=c.slice(0,-1)+pc(p));const g=o.move(i+i);return s(),n.attentionEncodeSurroundingInfo={after:m.outside,before:f.outside},l+c+g}function HH(t,e,n){return n.options.strong||"*"}function zH(t,e,n,r){return n.safe(t.value,r)}function jH(t){const e=t.options.ruleRepetition||3;if(e<3)throw new Error("Cannot serialize rules with repetition `"+e+"` for `options.ruleRepetition`, expected `3` or more");return e}function $H(t,e,n){const r=(zk(n)+(n.options.ruleSpaces?" ":"")).repeat(jH(n));return n.options.ruleSpaces?r.slice(0,-1):r}const $k={blockquote:fH,break:Zw,code:EH,definition:xH,emphasis:Mk,hardBreak:Zw,heading:SH,html:Dk,image:Lk,imageReference:Pk,inlineCode:Fk,link:Uk,linkReference:Hk,list:MH,listItem:LH,paragraph:PH,root:BH,strong:jk,text:zH,thematicBreak:$H};function WH(){return{enter:{table:VH,tableData:Jw,tableHeader:Jw,tableRow:KH},exit:{codeText:YH,table:GH,tableData:Zg,tableHeader:Zg,tableRow:Zg}}}function VH(t){const e=t._align;this.enter({type:"table",align:e.map(function(n){return n==="none"?null:n}),children:[]},t),this.data.inTable=!0}function GH(t){this.exit(t),this.data.inTable=void 0}function KH(t){this.enter({type:"tableRow",children:[]},t)}function Zg(t){this.exit(t)}function Jw(t){this.enter({type:"tableCell",children:[]},t)}function YH(t){let e=this.resume();this.data.inTable&&(e=e.replace(/\\([\\|])/g,qH));const n=this.stack[this.stack.length-1];n.type,n.value=e,this.exit(t)}function qH(t,e){return e==="|"?e:t}function XH(t){const e=t||{},n=e.tableCellPadding,r=e.tablePipeAlign,i=e.stringLength,s=n?" ":"|";return{unsafe:[{character:"\r",inConstruct:"tableCell"},{character:` `,inConstruct:"tableCell"},{atBreak:!0,character:"|",after:"[ :-]"},{character:"|",inConstruct:"tableCell"},{atBreak:!0,character:":",after:"-"},{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{inlineCode:m,table:o,tableCell:c,tableRow:l}};function o(g,x,v,S){return d(f(g,v,S),g.align)}function l(g,x,v,S){const C=p(g,v,S),A=d([C]);return A.slice(0,A.indexOf(` `))}function c(g,x,v,S){const C=v.enter("tableCell"),A=v.enter("phrasing"),k=v.containerPhrasing(g,{...S,before:s,after:s});return A(),C(),k}function d(g,x){return cH(g,{align:x,alignDelimiters:r,padding:n,stringLength:i})}function f(g,x,v){const S=g.children;let C=-1;const A=[],k=x.enter("table");for(;++C0&&!n&&(t[t.length-1][1]._gfmAutolinkLiteralWalkedInto=!0),n}const pz={tokenize:wz,partial:!0};function mz(){return{document:{91:{name:"gfmFootnoteDefinition",tokenize:yz,continuation:{tokenize:xz},exit:vz}},text:{91:{name:"gfmFootnoteCall",tokenize:Ez},93:{name:"gfmPotentialFootnoteCall",add:"after",tokenize:gz,resolveTo:bz}}}}function gz(t,e,n){const r=this;let i=r.events.length;const s=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let o;for(;i--;){const c=r.events[i][1];if(c.type==="labelImage"){o=c;break}if(c.type==="gfmFootnoteCall"||c.type==="labelLink"||c.type==="label"||c.type==="image"||c.type==="link")break}return l;function l(c){if(!o||!o._balanced)return n(c);const d=vi(r.sliceSerialize({start:o.end,end:r.now()}));return d.codePointAt(0)!==94||!s.includes(d.slice(1))?n(c):(t.enter("gfmFootnoteCallLabelMarker"),t.consume(c),t.exit("gfmFootnoteCallLabelMarker"),e(c))}}function bz(t,e){let n=t.length;for(;n--;)if(t[n][1].type==="labelImage"&&t[n][0]==="enter"){t[n][1];break}t[n+1][1].type="data",t[n+3][1].type="gfmFootnoteCallLabelMarker";const r={type:"gfmFootnoteCall",start:Object.assign({},t[n+3][1].start),end:Object.assign({},t[t.length-1][1].end)},i={type:"gfmFootnoteCallMarker",start:Object.assign({},t[n+3][1].end),end:Object.assign({},t[n+3][1].end)};i.end.column++,i.end.offset++,i.end._bufferIndex++;const s={type:"gfmFootnoteCallString",start:Object.assign({},i.end),end:Object.assign({},t[t.length-1][1].start)},o={type:"chunkString",contentType:"string",start:Object.assign({},s.start),end:Object.assign({},s.end)},l=[t[n+1],t[n+2],["enter",r,e],t[n+3],t[n+4],["enter",i,e],["exit",i,e],["enter",s,e],["enter",o,e],["exit",o,e],["exit",s,e],t[t.length-2],t[t.length-1],["exit",r,e]];return t.splice(n,t.length-n+1,...l),t}function Ez(t,e,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let s=0,o;return l;function l(p){return t.enter("gfmFootnoteCall"),t.enter("gfmFootnoteCallLabelMarker"),t.consume(p),t.exit("gfmFootnoteCallLabelMarker"),c}function c(p){return p!==94?n(p):(t.enter("gfmFootnoteCallMarker"),t.consume(p),t.exit("gfmFootnoteCallMarker"),t.enter("gfmFootnoteCallString"),t.enter("chunkString").contentType="string",d)}function d(p){if(s>999||p===93&&!o||p===null||p===91||Kt(p))return n(p);if(p===93){t.exit("chunkString");const m=t.exit("gfmFootnoteCallString");return i.includes(vi(r.sliceSerialize(m)))?(t.enter("gfmFootnoteCallLabelMarker"),t.consume(p),t.exit("gfmFootnoteCallLabelMarker"),t.exit("gfmFootnoteCall"),e):n(p)}return Kt(p)||(o=!0),s++,t.consume(p),p===92?f:d}function f(p){return p===91||p===92||p===93?(t.consume(p),s++,d):d(p)}}function yz(t,e,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let s,o=0,l;return c;function c(x){return t.enter("gfmFootnoteDefinition")._container=!0,t.enter("gfmFootnoteDefinitionLabel"),t.enter("gfmFootnoteDefinitionLabelMarker"),t.consume(x),t.exit("gfmFootnoteDefinitionLabelMarker"),d}function d(x){return x===94?(t.enter("gfmFootnoteDefinitionMarker"),t.consume(x),t.exit("gfmFootnoteDefinitionMarker"),t.enter("gfmFootnoteDefinitionLabelString"),t.enter("chunkString").contentType="string",f):n(x)}function f(x){if(o>999||x===93&&!l||x===null||x===91||Kt(x))return n(x);if(x===93){t.exit("chunkString");const v=t.exit("gfmFootnoteDefinitionLabelString");return s=vi(r.sliceSerialize(v)),t.enter("gfmFootnoteDefinitionLabelMarker"),t.consume(x),t.exit("gfmFootnoteDefinitionLabelMarker"),t.exit("gfmFootnoteDefinitionLabel"),m}return Kt(x)||(l=!0),o++,t.consume(x),x===92?p:f}function p(x){return x===91||x===92||x===93?(t.consume(x),o++,f):f(x)}function m(x){return x===58?(t.enter("definitionMarker"),t.consume(x),t.exit("definitionMarker"),i.includes(s)||i.push(s),Nt(t,g,"gfmFootnoteDefinitionWhitespace")):n(x)}function g(x){return e(x)}}function xz(t,e,n){return t.check(Dc,e,t.attempt(pz,e,n))}function vz(t){t.exit("gfmFootnoteDefinition")}function wz(t,e,n){const r=this;return Nt(t,i,"gfmFootnoteDefinitionIndent",5);function i(s){const o=r.events[r.events.length-1];return o&&o[1].type==="gfmFootnoteDefinitionIndent"&&o[2].sliceSerialize(o[1],!0).length===4?e(s):n(s)}}function Tz(t){let n=(t||{}).singleTilde;const r={name:"strikethrough",tokenize:s,resolveAll:i};return n==null&&(n=!0),{text:{126:r},insideSpan:{null:[r]},attentionMarkers:{null:[126]}};function i(o,l){let c=-1;for(;++c1?c(x):(o.consume(x),p++,g);if(p<2&&!n)return c(x);const S=o.exit("strikethroughSequenceTemporary"),C=bl(x);return S._open=!C||C===2&&!!v,S._close=!v||v===2&&!!C,l(x)}}}class Sz{constructor(){this.map=[]}add(e,n,r){_z(this,e,n,r)}consume(e){if(this.map.sort(function(s,o){return s[0]-o[0]}),this.map.length===0)return;let n=this.map.length;const r=[];for(;n>0;)n-=1,r.push(e.slice(this.map[n][0]+this.map[n][1]),this.map[n][2]),e.length=this.map[n][0];r.push(e.slice()),e.length=0;let i=r.pop();for(;i;){for(const s of i)e.push(s);i=r.pop()}this.map.length=0}}function _z(t,e,n,r){let i=0;if(!(n===0&&r.length===0)){for(;i-1;){const Z=r.events[Y][1].type;if(Z==="lineEnding"||Z==="linePrefix")Y--;else break}const z=Y>-1?r.events[Y][1].type:null,ie=z==="tableHead"||z==="tableRow"?I:c;return ie===I&&r.parser.lazy[r.now().line]?n(P):ie(P)}function c(P){return t.enter("tableHead"),t.enter("tableRow"),d(P)}function d(P){return P===124||(o=!0,s+=1),f(P)}function f(P){return P===null?n(P):et(P)?s>1?(s=0,r.interrupt=!0,t.exit("tableRow"),t.enter("lineEnding"),t.consume(P),t.exit("lineEnding"),g):n(P):St(P)?Nt(t,f,"whitespace")(P):(s+=1,o&&(o=!1,i+=1),P===124?(t.enter("tableCellDivider"),t.consume(P),t.exit("tableCellDivider"),o=!0,f):(t.enter("data"),p(P)))}function p(P){return P===null||P===124||Kt(P)?(t.exit("data"),f(P)):(t.consume(P),P===92?m:p)}function m(P){return P===92||P===124?(t.consume(P),p):p(P)}function g(P){return r.interrupt=!1,r.parser.lazy[r.now().line]?n(P):(t.enter("tableDelimiterRow"),o=!1,St(P)?Nt(t,x,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(P):x(P))}function x(P){return P===45||P===58?S(P):P===124?(o=!0,t.enter("tableCellDivider"),t.consume(P),t.exit("tableCellDivider"),v):F(P)}function v(P){return St(P)?Nt(t,S,"whitespace")(P):S(P)}function S(P){return P===58?(s+=1,o=!0,t.enter("tableDelimiterMarker"),t.consume(P),t.exit("tableDelimiterMarker"),C):P===45?(s+=1,C(P)):P===null||et(P)?M(P):F(P)}function C(P){return P===45?(t.enter("tableDelimiterFiller"),A(P)):F(P)}function A(P){return P===45?(t.consume(P),A):P===58?(o=!0,t.exit("tableDelimiterFiller"),t.enter("tableDelimiterMarker"),t.consume(P),t.exit("tableDelimiterMarker"),k):(t.exit("tableDelimiterFiller"),k(P))}function k(P){return St(P)?Nt(t,M,"whitespace")(P):M(P)}function M(P){return P===124?x(P):P===null||et(P)?!o||i!==s?F(P):(t.exit("tableDelimiterRow"),t.exit("tableHead"),e(P)):F(P)}function F(P){return n(P)}function I(P){return t.enter("tableRow"),D(P)}function D(P){return P===124?(t.enter("tableCellDivider"),t.consume(P),t.exit("tableCellDivider"),D):P===null||et(P)?(t.exit("tableRow"),e(P)):St(P)?Nt(t,D,"whitespace")(P):(t.enter("data"),G(P))}function G(P){return P===null||P===124||Kt(P)?(t.exit("data"),D(P)):(t.consume(P),P===92?X:G)}function X(P){return P===92||P===124?(t.consume(P),G):G(P)}}function Nz(t,e){let n=-1,r=!0,i=0,s=[0,0,0,0],o=[0,0,0,0],l=!1,c=0,d,f,p;const m=new Sz;for(;++nn[2]+1){const x=n[2]+1,v=n[3]-n[2]-1;t.add(x,v,[])}}t.add(n[3]+1,0,[["exit",p,e]])}return i!==void 0&&(s.end=Object.assign({},Va(e.events,i)),t.add(i,0,[["exit",s,e]]),s=void 0),s}function tT(t,e,n,r,i){const s=[],o=Va(e.events,n);i&&(i.end=Object.assign({},o),s.push(["exit",i,e])),r.end=Object.assign({},o),s.push(["exit",r,e]),t.add(n+1,0,s)}function Va(t,e){const n=t[e],r=n[0]==="enter"?"start":"end";return n[1][r]}const Rz={name:"tasklistCheck",tokenize:Oz};function Iz(){return{text:{91:Rz}}}function Oz(t,e,n){const r=this;return i;function i(c){return r.previous!==null||!r._gfmTasklistFirstContentOfListItem?n(c):(t.enter("taskListCheck"),t.enter("taskListCheckMarker"),t.consume(c),t.exit("taskListCheckMarker"),s)}function s(c){return Kt(c)?(t.enter("taskListCheckValueUnchecked"),t.consume(c),t.exit("taskListCheckValueUnchecked"),o):c===88||c===120?(t.enter("taskListCheckValueChecked"),t.consume(c),t.exit("taskListCheckValueChecked"),o):n(c)}function o(c){return c===93?(t.enter("taskListCheckMarker"),t.consume(c),t.exit("taskListCheckMarker"),t.exit("taskListCheck"),l):n(c)}function l(c){return et(c)?e(c):St(c)?t.check({tokenize:Mz},e,n)(c):n(c)}}function Mz(t,e,n){return Nt(t,r,"whitespace");function r(i){return i===null?n(i):e(i)}}function Dz(t){return ak([sz(),mz(),Tz(t),Az(),Iz()])}const Lz={};function Pz(t){const e=this,n=t||Lz,r=e.data(),i=r.micromarkExtensions||(r.micromarkExtensions=[]),s=r.fromMarkdownExtensions||(r.fromMarkdownExtensions=[]),o=r.toMarkdownExtensions||(r.toMarkdownExtensions=[]);i.push(Dz(n)),s.push(tz()),o.push(nz(n))}const nT=(function(t,e,n){const r=Lc(n);if(!t||!t.type||!t.children)throw new Error("Expected parent node");if(typeof e=="number"){if(e<0||e===Number.POSITIVE_INFINITY)throw new Error("Expected positive finite number as index")}else if(e=t.children.indexOf(e),e<0)throw new Error("Expected child node or index");for(;++ed&&(d=f):f&&(d!==void 0&&d>-1&&c.push(` `.repeat(d)||" "),d=-1,c.push(f))}return c.join("")}function Jk(t,e,n){return t.type==="element"?Wz(t,e,n):t.type==="text"?n.whitespace==="normal"?eN(t,n):Vz(t):[]}function Wz(t,e,n){const r=tN(t,n),i=t.children||[];let s=-1,o=[];if(jz(t))return o;let l,c;for(nb(t)||oT(t)&&nT(e,t,oT)?c=` `:zz(t)?(l=2,c=2):Zk(t)&&(l=1,c=1);++s]+>")+")",l={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},d={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[t.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+"\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)"+"|.)",end:"'",illegal:"."},t.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},f={className:"number",variants:[{begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)"},{begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)"}],relevance:0},p={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},t.inherit(d,{className:"string"}),{className:"string",begin:/<.*?>/},n,t.C_BLOCK_COMMENT_MODE]},m={className:"title",begin:e.optional(i)+t.IDENT_RE,relevance:0},g=e.optional(i)+t.IDENT_RE+"\\s*\\(",x=["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],v=["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],S=["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","flat_map","flat_set","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"],C=["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"],M={type:v,keyword:x,literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],_type_hints:S},F={className:"function.dispatch",relevance:0,keywords:{_hint:C},begin:e.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,t.IDENT_RE,e.lookahead(/(<[^<>]+>|)\s*\(/))},I=[F,p,l,n,t.C_BLOCK_COMMENT_MODE,f,d],D={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:M,contains:I.concat([{begin:/\(/,end:/\)/,keywords:M,contains:I.concat(["self"]),relevance:0}]),relevance:0},G={className:"function",begin:"("+o+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:M,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:M,relevance:0},{begin:g,returnBegin:!0,contains:[m],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[d,f]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:M,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,d,f,l,{begin:/\(/,end:/\)/,keywords:M,relevance:0,contains:["self",n,t.C_BLOCK_COMMENT_MODE,d,f,l]}]},l,n,t.C_BLOCK_COMMENT_MODE,p]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:M,illegal:"",keywords:M,contains:["self",l]},{begin:t.IDENT_RE+"::",keywords:M},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}}function Qz(t){const e={type:["boolean","byte","word","String"],built_in:["KeyboardController","MouseController","SoftwareSerial","EthernetServer","EthernetClient","LiquidCrystal","RobotControl","GSMVoiceCall","EthernetUDP","EsploraTFT","HttpClient","RobotMotor","WiFiClient","GSMScanner","FileSystem","Scheduler","GSMServer","YunClient","YunServer","IPAddress","GSMClient","GSMModem","Keyboard","Ethernet","Console","GSMBand","Esplora","Stepper","Process","WiFiUDP","GSM_SMS","Mailbox","USBHost","Firmata","PImage","Client","Server","GSMPIN","FileIO","Bridge","Serial","EEPROM","Stream","Mouse","Audio","Servo","File","Task","GPRS","WiFi","Wire","TFT","GSM","SPI","SD"],_hints:["setup","loop","runShellCommandAsynchronously","analogWriteResolution","retrieveCallingNumber","printFirmwareVersion","analogReadResolution","sendDigitalPortPair","noListenOnLocalhost","readJoystickButton","setFirmwareVersion","readJoystickSwitch","scrollDisplayRight","getVoiceCallStatus","scrollDisplayLeft","writeMicroseconds","delayMicroseconds","beginTransmission","getSignalStrength","runAsynchronously","getAsynchronously","listenOnLocalhost","getCurrentCarrier","readAccelerometer","messageAvailable","sendDigitalPorts","lineFollowConfig","countryNameWrite","runShellCommand","readStringUntil","rewindDirectory","readTemperature","setClockDivider","readLightSensor","endTransmission","analogReference","detachInterrupt","countryNameRead","attachInterrupt","encryptionType","readBytesUntil","robotNameWrite","readMicrophone","robotNameRead","cityNameWrite","userNameWrite","readJoystickY","readJoystickX","mouseReleased","openNextFile","scanNetworks","noInterrupts","digitalWrite","beginSpeaker","mousePressed","isActionDone","mouseDragged","displayLogos","noAutoscroll","addParameter","remoteNumber","getModifiers","keyboardRead","userNameRead","waitContinue","processInput","parseCommand","printVersion","readNetworks","writeMessage","blinkVersion","cityNameRead","readMessage","setDataMode","parsePacket","isListening","setBitOrder","beginPacket","isDirectory","motorsWrite","drawCompass","digitalRead","clearScreen","serialEvent","rightToLeft","setTextSize","leftToRight","requestFrom","keyReleased","compassRead","analogWrite","interrupts","WiFiServer","disconnect","playMelody","parseFloat","autoscroll","getPINUsed","setPINUsed","setTimeout","sendAnalog","readSlider","analogRead","beginWrite","createChar","motorsStop","keyPressed","tempoWrite","readButton","subnetMask","debugPrint","macAddress","writeGreen","randomSeed","attachGPRS","readString","sendString","remotePort","releaseAll","mouseMoved","background","getXChange","getYChange","answerCall","getResult","voiceCall","endPacket","constrain","getSocket","writeJSON","getButton","available","connected","findUntil","readBytes","exitValue","readGreen","writeBlue","startLoop","IPAddress","isPressed","sendSysex","pauseMode","gatewayIP","setCursor","getOemKey","tuneWrite","noDisplay","loadImage","switchPIN","onRequest","onReceive","changePIN","playFile","noBuffer","parseInt","overflow","checkPIN","knobRead","beginTFT","bitClear","updateIR","bitWrite","position","writeRGB","highByte","writeRed","setSpeed","readBlue","noStroke","remoteIP","transfer","shutdown","hangCall","beginSMS","endWrite","attached","maintain","noCursor","checkReg","checkPUK","shiftOut","isValid","shiftIn","pulseIn","connect","println","localIP","pinMode","getIMEI","display","noBlink","process","getBand","running","beginSD","drawBMP","lowByte","setBand","release","bitRead","prepare","pointTo","readRed","setMode","noFill","remove","listen","stroke","detach","attach","noTone","exists","buffer","height","bitSet","circle","config","cursor","random","IRread","setDNS","endSMS","getKey","micros","millis","begin","print","write","ready","flush","width","isPIN","blink","clear","press","mkdir","rmdir","close","point","yield","image","BSSID","click","delay","read","text","move","peek","beep","rect","line","open","seek","fill","size","turn","stop","home","find","step","tone","sqrt","RSSI","SSID","end","bit","tan","cos","sin","pow","map","abs","max","min","get","run","put"],literal:["DIGITAL_MESSAGE","FIRMATA_STRING","ANALOG_MESSAGE","REPORT_DIGITAL","REPORT_ANALOG","INPUT_PULLUP","SET_PIN_MODE","INTERNAL2V56","SYSTEM_RESET","LED_BUILTIN","INTERNAL1V1","SYSEX_START","INTERNAL","EXTERNAL","DEFAULT","OUTPUT","INPUT","HIGH","LOW"]},n=Xz(t),r=n.keywords;return r.type=[...r.type,...e.type],r.literal=[...r.literal,...e.literal],r.built_in=[...r.built_in,...e.built_in],r._hints=e._hints,n.name="Arduino",n.aliases=["ino"],n.supersetOf="cpp",n}function Zz(t){const e=t.regex,n={},r={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{begin:e.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},r]});const i={className:"subst",begin:/\$\(/,end:/\)/,contains:[t.BACKSLASH_ESCAPE]},s=t.inherit(t.COMMENT(),{match:[/(^|\s)/,/#.*$/],scope:{2:"comment"}}),o={begin:/<<-?\s*(?=\w+)/,starts:{contains:[t.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},l={className:"string",begin:/"/,end:/"/,contains:[t.BACKSLASH_ESCAPE,n,i]};i.contains.push(l);const c={match:/\\"/},d={className:"string",begin:/'/,end:/'/},f={match:/\\'/},p={begin:/\$?\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},t.NUMBER_MODE,n]},m=["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"],g=t.SHEBANG({binary:`(${m.join("|")})`,relevance:10}),x={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[t.inherit(t.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0},v=["if","then","else","elif","fi","time","for","while","until","in","do","done","case","esac","coproc","function","select"],S=["true","false"],C={match:/(\/[a-z._-]+)+/},A=["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset"],k=["alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","sudo","type","typeset","ulimit","unalias"],M=["autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp"],F=["chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"];return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/,keyword:v,literal:S,built_in:[...A,...k,"set","shopt",...M,...F]},contains:[g,t.SHEBANG(),x,p,s,o,C,l,c,d,f,n]}}function Jz(t){const e=t.regex,n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),r="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",o="("+r+"|"+e.optional(i)+"[a-zA-Z_]\\w*"+e.optional("<[^<>]+>")+")",l={className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{match:/\batomic_[a-z]{3,6}\b/}]},d={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[t.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+"\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)"+"|.)",end:"'",illegal:"."},t.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},f={className:"number",variants:[{match:/\b(0b[01']+)/},{match:/(-?)\b([\d']+(\.[\d']*)?|\.[\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)/},{match:/(-?)\b(0[xX][a-fA-F0-9]+(?:'[a-fA-F0-9]+)*(?:\.[a-fA-F0-9]*(?:'[a-fA-F0-9]*)*)?(?:[pP][-+]?[0-9]+)?(l|L)?(u|U)?)/},{match:/(-?)\b\d+(?:'\d+)*(?:\.\d*(?:'\d*)*)?(?:[eE][-+]?\d+)?/}],relevance:0},p={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef elifdef elifndef include"},contains:[{begin:/\\\n/,relevance:0},t.inherit(d,{className:"string"}),{className:"string",begin:/<.*?>/},n,t.C_BLOCK_COMMENT_MODE]},m={className:"title",begin:e.optional(i)+t.IDENT_RE,relevance:0},g=e.optional(i)+t.IDENT_RE+"\\s*\\(",S={keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","typeof","typeof_unqual","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_BitInt","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal96","_Decimal128","_Decimal64x","_Decimal128x","_Float16","_Float32","_Float64","_Float128","_Float32x","_Float64x","_Float128x","const","static","constexpr","complex","bool","imaginary"],literal:"true false NULL",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"},C=[p,l,n,t.C_BLOCK_COMMENT_MODE,f,d],A={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:S,contains:C.concat([{begin:/\(/,end:/\)/,keywords:S,contains:C.concat(["self"]),relevance:0}]),relevance:0},k={begin:"("+o+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:S,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:S,relevance:0},{begin:g,returnBegin:!0,contains:[t.inherit(m,{className:"title.function"})],relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:S,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,d,f,l,{begin:/\(/,end:/\)/,keywords:S,relevance:0,contains:["self",n,t.C_BLOCK_COMMENT_MODE,d,f,l]}]},l,n,t.C_BLOCK_COMMENT_MODE,p]};return{name:"C",aliases:["h"],keywords:S,disableAutodetect:!0,illegal:"=]/,contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{preprocessor:p,strings:d,keywords:S}}}function e7(t){const e=t.regex,n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),r="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",o="(?!struct)("+r+"|"+e.optional(i)+"[a-zA-Z_]\\w*"+e.optional("<[^<>]+>")+")",l={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},d={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[t.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+"\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)"+"|.)",end:"'",illegal:"."},t.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},f={className:"number",variants:[{begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)"},{begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)"}],relevance:0},p={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},t.inherit(d,{className:"string"}),{className:"string",begin:/<.*?>/},n,t.C_BLOCK_COMMENT_MODE]},m={className:"title",begin:e.optional(i)+t.IDENT_RE,relevance:0},g=e.optional(i)+t.IDENT_RE+"\\s*\\(",x=["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],v=["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],S=["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","flat_map","flat_set","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"],C=["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"],M={type:v,keyword:x,literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],_type_hints:S},F={className:"function.dispatch",relevance:0,keywords:{_hint:C},begin:e.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,t.IDENT_RE,e.lookahead(/(<[^<>]+>|)\s*\(/))},I=[F,p,l,n,t.C_BLOCK_COMMENT_MODE,f,d],D={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:M,contains:I.concat([{begin:/\(/,end:/\)/,keywords:M,contains:I.concat(["self"]),relevance:0}]),relevance:0},G={className:"function",begin:"("+o+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:M,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:r,keywords:M,relevance:0},{begin:g,returnBegin:!0,contains:[m],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[d,f]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:M,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,d,f,l,{begin:/\(/,end:/\)/,keywords:M,relevance:0,contains:["self",n,t.C_BLOCK_COMMENT_MODE,d,f,l]}]},l,n,t.C_BLOCK_COMMENT_MODE,p]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:M,illegal:"",keywords:M,contains:["self",l]},{begin:t.IDENT_RE+"::",keywords:M},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}}function t7(t){const e=["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"],n=["public","private","protected","static","internal","protected","abstract","async","extern","override","unsafe","virtual","new","sealed","partial"],r=["default","false","null","true"],i=["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"],s=["add","alias","and","ascending","args","async","await","by","descending","dynamic","equals","file","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","record","remove","required","scoped","select","set","unmanaged","value|0","var","when","where","with","yield"],o={keyword:i.concat(s),built_in:e,literal:r},l=t.inherit(t.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),c={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},d={className:"string",begin:/"""("*)(?!")(.|\n)*?"""\1/,relevance:1},f={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},p=t.inherit(f,{illegal:/\n/}),m={className:"subst",begin:/\{/,end:/\}/,keywords:o},g=t.inherit(m,{illegal:/\n/}),x={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},t.BACKSLASH_ESCAPE,g]},v={className:"string",begin:/\$@"/,end:'"',contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},m]},S=t.inherit(v,{illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},g]});m.contains=[v,x,f,t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,c,t.C_BLOCK_COMMENT_MODE],g.contains=[S,x,p,t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,c,t.inherit(t.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];const C={variants:[d,v,x,f,t.APOS_STRING_MODE,t.QUOTE_STRING_MODE]},A={begin:"<",end:">",contains:[{beginKeywords:"in out"},l]},k=t.IDENT_RE+"(<"+t.IDENT_RE+"(\\s*,\\s*"+t.IDENT_RE+")*>)?(\\[\\])?",M={begin:"@"+t.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:o,illegal:/::/,contains:[t.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:""},{begin:""}]}]}),t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{keyword:"if else elif endif define undef warning error line region endregion pragma checksum"}},C,c,{beginKeywords:"class interface",relevance:0,end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},l,A,t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[l,t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE]},{beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,contains:[l,A,t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+k+"\\s+)+"+t.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:o,contains:[{beginKeywords:n.join(" "),relevance:0},{begin:t.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,contains:[t.TITLE_MODE,A],relevance:0},{match:/\(\)/},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,relevance:0,contains:[C,c,t.C_BLOCK_COMMENT_MODE]},t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE]},M]}}const n7=t=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:t.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:t.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),r7=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],i7=["defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],s7=[...r7,...i7],o7=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),a7=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),l7=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),u7=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse();function c7(t){const e=t.regex,n=n7(t),r={begin:/-(webkit|moz|ms|o)-(?=[a-z])/},i="and or not only",s=/@-?\w[\w]*(-\w+)*/,o="[a-zA-Z-][a-zA-Z0-9_-]*",l=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE];return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"},contains:[n.BLOCK_COMMENT,r,n.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:"selector-class",begin:"\\."+o,relevance:0},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{begin:":("+a7.join("|")+")"},{begin:":(:)?("+l7.join("|")+")"}]},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+u7.join("|")+")\\b"},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,n.HEXCOLOR,n.IMPORTANT,n.CSS_NUMBER_MODE,...l,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"},contains:[...l,{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},n.FUNCTION_DISPATCH]},{begin:e.lookahead(/@/),end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword",begin:s},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:i,attribute:o7.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"},...l,n.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"\\b("+s7.join("|")+")\\b"}]}}function d7(t){const e=t.regex;return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,match:e.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)},{className:"comment",variants:[{begin:e.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,end:/$/}]}}function f7(t){const s={keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"],type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"],literal:["true","false","iota","nil"],built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"]};return{name:"Go",aliases:["golang"],keywords:s,illegal:"nN(t,e,n-1))}function m7(t){const e=t.regex,n="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",r=n+nN("(?:<"+n+"~~~(?:\\s*,\\s*"+n+"~~~)*>)?",/~~~/g,2),c={keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits","goto","when"],literal:["false","true","null"],type:["char","boolean","long","float","int","byte","short","double"],built_in:["super","this"]},d={className:"meta",begin:"@"+n,contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},f={className:"params",begin:/\(/,end:/\)/,keywords:c,relevance:0,contains:[t.C_BLOCK_COMMENT_MODE],endsParent:!0};return{name:"Java",aliases:["jsp"],keywords:c,illegal:/<\/|#/,contains:[t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{begin:/import java\.[a-z]+\./,keywords:"import",relevance:2},t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/,className:"string",contains:[t.BACKSLASH_ESCAPE]},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,{match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,n],className:{1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{begin:[e.concat(/(?!else)/,n),/\s+/,n,/\s+/,/=(?!=)/],className:{1:"type",3:"variable",5:"operator"}},{begin:[/record/,/\s+/,n],className:{1:"keyword",3:"title.class"},contains:[f,t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE]},{beginKeywords:"new throw return else",relevance:0},{begin:["(?:"+r+"\\s+)",t.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{2:"title.function"},keywords:c,contains:[{className:"params",begin:/\(/,end:/\)/,keywords:c,relevance:0,contains:[d,t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,aT,t.C_BLOCK_COMMENT_MODE]},t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE]},aT,d]}}const lT="[A-Za-z$_][0-9A-Za-z$_]*",g7=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],b7=["true","false","null","undefined","NaN","Infinity"],rN=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],iN=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],sN=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],E7=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],y7=[].concat(sN,rN,iN);function x7(t){const e=t.regex,n=(j,{after:W})=>{const O="",end:""},s=/<[A-Za-z0-9\\._:-]+\s*\/>/,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(j,W)=>{const O=j[0].length+j.index,U=j.input[O];if(U==="<"||U===","){W.ignoreMatch();return}U===">"&&(n(j,{after:O})||W.ignoreMatch());let Q;const R=j.input.substring(O);if(Q=R.match(/^\s*=/)){W.ignoreMatch();return}if((Q=R.match(/^\s+extends\s+/))&&Q.index===0){W.ignoreMatch();return}}},l={$pattern:lT,keyword:g7,literal:b7,built_in:y7,"variable.language":E7},c="[0-9](_?[0-9])*",d=`\\.(${c})`,f="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",p={className:"number",variants:[{begin:`(\\b(${f})((${d})|\\.)?|(${d}))[eE][+-]?(${c})\\b`},{begin:`\\b(${f})\\b((${d})\\b|\\.)?|(${d})\\b`},{begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{begin:"\\b0[0-7]+n?\\b"}],relevance:0},m={className:"subst",begin:"\\$\\{",end:"\\}",keywords:l,contains:[]},g={begin:".?html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,m],subLanguage:"xml"}},x={begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,m],subLanguage:"css"}},v={begin:".?gql`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,m],subLanguage:"graphql"}},S={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,m]},A={className:"comment",variants:[t.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:"variable",begin:r+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]},k=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,g,x,v,S,{match:/\$\d+/},p];m.contains=k.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(k)});const M=[].concat(A,m.contains),F=M.concat([{begin:/(\s*)\(/,end:/\)/,keywords:l,contains:["self"].concat(M)}]),I={className:"params",begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:F},D={variants:[{match:[/class/,/\s+/,r,/\s+/,/extends/,/\s+/,e.concat(r,"(",e.concat(/\./,r),")*")],scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{match:[/class/,/\s+/,r],scope:{1:"keyword",3:"title.class"}}]},G={relevance:0,match:e.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),className:"title.class",keywords:{_:[...rN,...iN]}},X={label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},P={variants:[{match:[/function/,/\s+/,r,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:"keyword",3:"title.function"},label:"func.def",contains:[I],illegal:/%/},Y={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"};function z(j){return e.concat("(?!",j.join("|"),")")}const ie={match:e.concat(/\b/,z([...sN,"super","import"].map(j=>`${j}\\s*\\(`)),r,e.lookahead(/\s*\(/)),className:"title.function",relevance:0},Z={begin:e.concat(/\./,e.lookahead(e.concat(r,/(?![0-9A-Za-z$_(])/))),end:r,excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},ee={match:[/get|set/,/\s+/,r,/(?=\()/],className:{1:"keyword",3:"title.function"},contains:[{begin:/\(\)/},I]},ae="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",de={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,e.lookahead(ae)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[I]};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:F,CLASS_REFERENCE:G},illegal:/#(?![$_A-z])/,contains:[t.SHEBANG({label:"shebang",binary:"node",relevance:5}),X,t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,g,x,v,S,A,{match:/\$\d+/},p,G,{scope:"attr",match:r+e.lookahead(":"),relevance:0},de,{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[A,t.REGEXP_MODE,{className:"function",begin:ae,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:F}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:i.begin,end:i.end},{match:s},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,contains:["self"]}]}]},P,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+t.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[I,t.inherit(t.TITLE_MODE,{begin:r,className:"title.function"})]},{match:/\.\.\./,relevance:0},Z,{match:"\\$"+r,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[I]},ie,Y,D,ee,{match:/\$[(.]/}]}}function v7(t){const e={className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},n={match:/[{}[\],:]/,className:"punctuation",relevance:0},r=["true","false","null"],i={scope:"literal",beginKeywords:r.join(" ")};return{name:"JSON",aliases:["jsonc"],keywords:{literal:r},contains:[e,n,t.QUOTE_STRING_MODE,i,t.C_NUMBER_MODE,t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE],illegal:"\\S"}}var Ka="[0-9](_*[0-9])*",pf=`\\.(${Ka})`,mf="[0-9a-fA-F](_*[0-9a-fA-F])*",w7={className:"number",variants:[{begin:`(\\b(${Ka})((${pf})|\\.)?|(${pf}))[eE][+-]?(${Ka})[fFdD]?\\b`},{begin:`\\b(${Ka})((${pf})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${pf})[fFdD]?\\b`},{begin:`\\b(${Ka})[fFdD]\\b`},{begin:`\\b0[xX]((${mf})\\.?|(${mf})?\\.(${mf}))[pP][+-]?(${Ka})[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${mf})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};function T7(t){const e={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},n={className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},r={className:"symbol",begin:t.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:/\$\{/,end:/\}/,contains:[t.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+t.UNDERSCORE_IDENT_RE},o={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[t.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[t.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(o);const l={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+t.UNDERSCORE_IDENT_RE+")?"},c={className:"meta",begin:"@"+t.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[t.inherit(o,{className:"string"}),"self"]}]},d=w7,f=t.COMMENT("/\\*","\\*/",{contains:[t.C_BLOCK_COMMENT_MODE]}),p={variants:[{className:"type",begin:t.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},m=p;return m.variants[1].contains=[p],p.variants[1].contains=[m],{name:"Kotlin",aliases:["kt","kts"],keywords:e,contains:[t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),t.C_LINE_COMMENT_MODE,f,n,r,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:e,relevance:5,contains:[{begin:t.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[t.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:e,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[p,t.C_LINE_COMMENT_MODE,f],relevance:0},t.C_LINE_COMMENT_MODE,f,l,c,o,t.C_NUMBER_MODE]},f]},{begin:[/class|interface|trait/,/\s+/,t.UNDERSCORE_IDENT_RE],beginScope:{3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},t.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/,excludeBegin:!0,returnEnd:!0},l,c]},o,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:` `},d]}}const S7=t=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:t.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:t.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),_7=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],C7=["defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],A7=[..._7,...C7],k7=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),oN=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),aN=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),N7=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse(),R7=oN.concat(aN).sort().reverse();function I7(t){const e=S7(t),n=R7,r="and or not only",i="[\\w-]+",s="("+i+"|@\\{"+i+"\\})",o=[],l=[],c=function(k){return{className:"string",begin:"~?"+k+".*?"+k}},d=function(k,M,F){return{className:k,begin:M,relevance:F}},f={$pattern:/[a-z-]+/,keyword:r,attribute:k7.join(" ")},p={begin:"\\(",end:"\\)",contains:l,keywords:f,relevance:0};l.push(t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,c("'"),c('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},e.HEXCOLOR,p,d("variable","@@?"+i,10),d("variable","@\\{"+i+"\\}"),d("built_in","~?`[^`]*?`"),{className:"attribute",begin:i+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0},e.IMPORTANT,{beginKeywords:"and not"},e.FUNCTION_DISPATCH);const m=l.concat({begin:/\{/,end:/\}/,contains:o}),g={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(l)},x={begin:s+"\\s*:",returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/},e.CSS_VARIABLE,{className:"attribute",begin:"\\b("+N7.join("|")+")\\b",end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:l}}]},v={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",keywords:f,returnEnd:!0,contains:l,relevance:0}},S={className:"variable",variants:[{begin:"@"+i+"\\s*:",relevance:15},{begin:"@"+i}],starts:{end:"[;}]",returnEnd:!0,contains:m}},C={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:s,end:/\{/}],returnBegin:!0,returnEnd:!0,illegal:`[<='$"]`,relevance:0,contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,g,d("keyword","all\\b"),d("variable","@\\{"+i+"\\}"),{begin:"\\b("+A7.join("|")+")\\b",className:"selector-tag"},e.CSS_NUMBER_MODE,d("selector-tag",s,0),d("selector-id","#"+s),d("selector-class","\\."+s,0),d("selector-tag","&",0),e.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",begin:":("+oN.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+aN.join("|")+")"},{begin:/\(/,end:/\)/,relevance:0,contains:m},{begin:"!important"},e.FUNCTION_DISPATCH]},A={begin:i+`:(:)?(${n.join("|")})`,returnBegin:!0,contains:[C]};return o.push(t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,v,S,A,x,C,g,e.FUNCTION_DISPATCH),{name:"Less",case_insensitive:!0,illegal:`[=>'/<($"]`,contains:o}}function O7(t){const e="\\[=*\\[",n="\\]=*\\]",r={begin:e,end:n,contains:["self"]},i=[t.COMMENT("--(?!"+e+")","$"),t.COMMENT("--"+e,n,{contains:[r],relevance:10})];return{name:"Lua",aliases:["pluto"],keywords:{$pattern:t.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[t.inherit(t.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:i}].concat(i)},t.C_NUMBER_MODE,t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,{className:"string",begin:e,end:n,contains:[r],relevance:5}])}}function M7(t){const e={className:"variable",variants:[{begin:"\\$\\("+t.UNDERSCORE_IDENT_RE+"\\)",contains:[t.BACKSLASH_ESCAPE]},{begin:/\$[@%",subLanguage:"xml",relevance:0},r={begin:"^[-\\*]{3,}",end:"$"},i={className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},s={className:"bullet",begin:"^[ ]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},o={begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},l=/[A-Za-z][A-Za-z0-9+.-]*/,c={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,relevance:2},{begin:e.concat(/\[.+?\]\(/,l,/:\/\/.*?\)/),relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}]},d={className:"strong",contains:[],variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]},f={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{begin:/_(?![_\s])/,end:/_/,relevance:0}]},p=t.inherit(d,{contains:[]}),m=t.inherit(f,{contains:[]});d.contains.push(m),f.contains.push(p);let g=[n,c];return[d,f,p,m].forEach(C=>{C.contains=C.contains.concat(g)}),g=g.concat(d,f),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:g}]}]},n,s,d,f,{className:"quote",begin:"^>\\s+",contains:g,end:"$"},i,r,c,o,{scope:"literal",match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}function L7(t){const e={className:"built_in",begin:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},n=/[a-zA-Z@][a-zA-Z0-9_]*/,l={"variable.language":["this","super"],$pattern:n,keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"],literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"],built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"],type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"]},c={$pattern:n,keyword:["@interface","@class","@protocol","@implementation"]};return{name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"],keywords:l,illegal:"/,end:/$/,illegal:"\\n"},t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+c.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:c,contains:[t.UNDERSCORE_TITLE_MODE]},{begin:"\\."+t.UNDERSCORE_IDENT_RE,relevance:0}]}}function P7(t){const e=t.regex,n=["abs","accept","alarm","and","atan2","bind","binmode","bless","break","caller","chdir","chmod","chomp","chop","chown","chr","chroot","class","close","closedir","connect","continue","cos","crypt","dbmclose","dbmopen","defined","delete","die","do","dump","each","else","elsif","endgrent","endhostent","endnetent","endprotoent","endpwent","endservent","eof","eval","exec","exists","exit","exp","fcntl","field","fileno","flock","for","foreach","fork","format","formline","getc","getgrent","getgrgid","getgrnam","gethostbyaddr","gethostbyname","gethostent","getlogin","getnetbyaddr","getnetbyname","getnetent","getpeername","getpgrp","getpriority","getprotobyname","getprotobynumber","getprotoent","getpwent","getpwnam","getpwuid","getservbyname","getservbyport","getservent","getsockname","getsockopt","given","glob","gmtime","goto","grep","gt","hex","if","index","int","ioctl","join","keys","kill","last","lc","lcfirst","length","link","listen","local","localtime","log","lstat","lt","ma","map","method","mkdir","msgctl","msgget","msgrcv","msgsnd","my","ne","next","no","not","oct","open","opendir","or","ord","our","pack","package","pipe","pop","pos","print","printf","prototype","push","q|0","qq","quotemeta","qw","qx","rand","read","readdir","readline","readlink","readpipe","recv","redo","ref","rename","require","reset","return","reverse","rewinddir","rindex","rmdir","say","scalar","seek","seekdir","select","semctl","semget","semop","send","setgrent","sethostent","setnetent","setpgrp","setpriority","setprotoent","setpwent","setservent","setsockopt","shift","shmctl","shmget","shmread","shmwrite","shutdown","sin","sleep","socket","socketpair","sort","splice","split","sprintf","sqrt","srand","stat","state","study","sub","substr","symlink","syscall","sysopen","sysread","sysseek","system","syswrite","tell","telldir","tie","tied","time","times","tr","truncate","uc","ucfirst","umask","undef","unless","unlink","unpack","unshift","untie","until","use","utime","values","vec","wait","waitpid","wantarray","warn","when","while","write","x|0","xor","y|0"],r=/[dualxmsipngr]{0,12}/,i={$pattern:/[\w.]+/,keyword:n.join(" ")},s={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:i},o={begin:/->\{/,end:/\}/},l={scope:"attr",match:/\s+:\s*\w+(\s*\(.*?\))?/},c={scope:"variable",variants:[{begin:/\$\d/},{begin:e.concat(/[$%@](?!")(\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")},{begin:/[$%@](?!")[^\s\w{=]|\$=/,relevance:0}],contains:[l]},d={className:"number",variants:[{match:/0?\.[0-9][0-9_]+\b/},{match:/\bv?(0|[1-9][0-9_]*(\.[0-9_]+)?|[1-9][0-9_]*)\b/},{match:/\b0[0-7][0-7_]*\b/},{match:/\b0x[0-9a-fA-F][0-9a-fA-F_]*\b/},{match:/\b0b[0-1][0-1_]*\b/}],relevance:0},f=[t.BACKSLASH_ESCAPE,s,c],p=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],m=(v,S,C="\\1")=>{const A=C==="\\1"?C:e.concat(C,S);return e.concat(e.concat("(?:",v,")"),S,/(?:\\.|[^\\\/])*?/,A,/(?:\\.|[^\\\/])*?/,C,r)},g=(v,S,C)=>e.concat(e.concat("(?:",v,")"),S,/(?:\\.|[^\\\/])*?/,C,r),x=[c,t.HASH_COMMENT_MODE,t.COMMENT(/^=\w/,/=cut/,{endsWithParent:!0}),o,{className:"string",contains:f,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[t.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{begin:"-?\\w+\\s*=>",relevance:0}]},d,{begin:"(\\/\\/|"+t.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[t.HASH_COMMENT_MODE,{className:"regexp",variants:[{begin:m("s|tr|y",e.either(...p,{capture:!0}))},{begin:m("s|tr|y","\\(","\\)")},{begin:m("s|tr|y","\\[","\\]")},{begin:m("s|tr|y","\\{","\\}")}],relevance:2},{className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{begin:g("(?:m|qr)?",/\//,/\//)},{begin:g("m|qr",e.either(...p,{capture:!0}),/\1/)},{begin:g("m|qr",/\(/,/\)/)},{begin:g("m|qr",/\[/,/\]/)},{begin:g("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub method",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[t.TITLE_MODE,l]},{className:"class",beginKeywords:"class",end:"[;{]",excludeEnd:!0,relevance:5,contains:[t.TITLE_MODE,l,d]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return s.contains=x,o.contains=x,{name:"Perl",aliases:["pl","pm"],keywords:i,contains:x}}function F7(t){const e=t.regex,n=/(?![A-Za-z0-9])(?![$])/,r=e.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,n),i=e.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,n),s=e.concat(/[A-Z]+/,n),o={scope:"variable",match:"\\$+"+r},l={scope:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{begin:/\?>/}]},c={scope:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]},d=t.inherit(t.APOS_STRING_MODE,{illegal:null}),f=t.inherit(t.QUOTE_STRING_MODE,{illegal:null,contains:t.QUOTE_STRING_MODE.contains.concat(c)}),p={begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/,contains:t.QUOTE_STRING_MODE.contains.concat(c),"on:begin":(Z,ee)=>{ee.data._beginMatch=Z[1]||Z[2]},"on:end":(Z,ee)=>{ee.data._beginMatch!==Z[1]&&ee.ignoreMatch()}},m=t.END_SAME_AS_BEGIN({begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/}),g=`[ ]`,x={scope:"string",variants:[f,d,p,m]},v={scope:"number",variants:[{begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?"}],relevance:0},S=["false","null","true"],C=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],A=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],M={keyword:C,literal:(Z=>{const ee=[];return Z.forEach(ae=>{ee.push(ae),ae.toLowerCase()===ae?ee.push(ae.toUpperCase()):ee.push(ae.toLowerCase())}),ee})(S),built_in:A},F=Z=>Z.map(ee=>ee.replace(/\|\d+$/,"")),I={variants:[{match:[/new/,e.concat(g,"+"),e.concat("(?!",F(A).join("\\b|"),"\\b)"),i],scope:{1:"keyword",4:"title.class"}}]},D=e.concat(r,"\\b(?!\\()"),G={variants:[{match:[e.concat(/::/,e.lookahead(/(?!class\b)/)),D],scope:{2:"variable.constant"}},{match:[/::/,/class/],scope:{2:"variable.language"}},{match:[i,e.concat(/::/,e.lookahead(/(?!class\b)/)),D],scope:{1:"title.class",3:"variable.constant"}},{match:[i,e.concat("::",e.lookahead(/(?!class\b)/))],scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class",3:"variable.language"}}]},X={scope:"attr",match:e.concat(r,e.lookahead(":"),e.lookahead(/(?!::)/))},P={relevance:0,begin:/\(/,end:/\)/,keywords:M,contains:[X,o,G,t.C_BLOCK_COMMENT_MODE,x,v,I]},Y={relevance:0,match:[/\b/,e.concat("(?!fn\\b|function\\b|",F(C).join("\\b|"),"|",F(A).join("\\b|"),"\\b)"),r,e.concat(g,"*"),e.lookahead(/(?=\()/)],scope:{3:"title.function.invoke"},contains:[P]};P.contains.push(Y);const z=[X,G,t.C_BLOCK_COMMENT_MODE,x,v,I],ie={begin:e.concat(/#\[\s*\\?/,e.either(i,s)),beginScope:"meta",end:/]/,endScope:"meta",keywords:{literal:S,keyword:["new","array"]},contains:[{begin:/\[/,end:/]/,keywords:{literal:S,keyword:["new","array"]},contains:["self",...z]},...z,{scope:"meta",variants:[{match:i},{match:s}]}]};return{case_insensitive:!1,keywords:M,contains:[ie,t.HASH_COMMENT_MODE,t.COMMENT("//","$"),t.COMMENT("/\\*","\\*/",{contains:[{scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/,keywords:"__halt_compiler",starts:{scope:"comment",end:t.MATCH_NOTHING_RE,contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},l,{scope:"variable.language",match:/\$this\b/},o,Y,G,{match:[/const/,/\s/,r],scope:{1:"keyword",3:"variable.constant"}},I,{scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use"},t.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:M,contains:["self",ie,o,G,t.C_BLOCK_COMMENT_MODE,x,v]}]},{scope:"class",variants:[{beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait",illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{beginKeywords:"extends implements"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/,contains:[t.inherit(t.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{beginKeywords:"use",relevance:0,end:";",contains:[{match:/\b(as|const|function)\b/,scope:"keyword"},t.UNDERSCORE_TITLE_MODE]},x,v]}}function B7(t){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},t.inherit(t.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),t.inherit(t.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}function U7(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}function H7(t){const e=t.regex,n=new RegExp("[\\p{XID_Start}_]\\p{XID_Continue}*","u"),r=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],l={$pattern:/[A-Za-z]\w+|__\w+__/,keyword:r,built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"],literal:["__debug__","Ellipsis","False","None","NotImplemented","True"],type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"]},c={className:"meta",begin:/^(>>>|\.\.\.) /},d={className:"subst",begin:/\{/,end:/\}/,keywords:l,illegal:/#/},f={begin:/\{\{/,relevance:0},p={className:"string",contains:[t.BACKSLASH_ESCAPE],variants:[{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/,contains:[t.BACKSLASH_ESCAPE,c],relevance:10},{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/,contains:[t.BACKSLASH_ESCAPE,c],relevance:10},{begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/,contains:[t.BACKSLASH_ESCAPE,c,f,d]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/,end:/"""/,contains:[t.BACKSLASH_ESCAPE,c,f,d]},{begin:/([uU]|[rR])'/,end:/'/,relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/,end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/,contains:[t.BACKSLASH_ESCAPE,f,d]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/,contains:[t.BACKSLASH_ESCAPE,f,d]},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE]},m="[0-9](_?[0-9])*",g=`(\\b(${m}))?\\.(${m})|\\b(${m})\\.`,x=`\\b|${r.join("|")}`,v={className:"number",relevance:0,variants:[{begin:`(\\b(${m})|(${g}))[eE][+-]?(${m})[jJ]?(?=${x})`},{begin:`(${g})[jJ]?`},{begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${x})`},{begin:`\\b0[bB](_?[01])+[lL]?(?=${x})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${x})`},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${x})`},{begin:`\\b(${m})[jJ](?=${x})`}]},S={className:"comment",begin:e.lookahead(/# type:/),end:/$/,keywords:l,contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},C={className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:["self",c,v,p,t.HASH_COMMENT_MODE]}]};return d.contains=[p,v,c],{name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:l,illegal:/(<\/|\?)|=>/,contains:[c,v,{scope:"variable.language",match:/\bself\b/},{beginKeywords:"if",relevance:0},{match:/\bor\b/,scope:"keyword"},p,S,t.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,n],scope:{1:"keyword",3:"title.function"},contains:[C]},{variants:[{match:[/\bclass/,/\s+/,n,/\s*/,/\(\s*/,n,/\s*\)/]},{match:[/\bclass/,/\s+/,n]}],scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[v,C,p]}]}}function z7(t){return{aliases:["pycon"],contains:[{className:"meta.prompt",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}function j7(t){const e=t.regex,n=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,r=e.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,s=e.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/);return{name:"R",keywords:{$pattern:n,keyword:"function if in break next repeat else for while",literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10",built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm"},contains:[t.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/,starts:{end:e.lookahead(e.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)),endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{scope:"variable",variants:[{match:n},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}]}),t.HASH_COMMENT_MODE,{scope:"string",contains:[t.BACKSLASH_ESCAPE],variants:[t.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/}),t.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/}),t.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/}),t.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/}),t.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/}),t.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"',relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{1:"operator",2:"number"},match:[i,r]},{scope:{1:"operator",2:"number"},match:[/%[^%]*%/,r]},{scope:{1:"punctuation",2:"number"},match:[s,r]},{scope:{2:"number"},match:[/[^a-zA-Z0-9._]|^/,r]}]},{scope:{3:"operator"},match:[n,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:s},{begin:"`",end:"`",contains:[{begin:/\\./}]}]}}function $7(t){const e=t.regex,n="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",r=e.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=e.concat(r,/(::\w+)*/),o={"variable.constant":["__FILE__","__LINE__","__ENCODING__"],"variable.language":["self","super"],keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield",...["include","extend","prepend","public","private","protected","raise","throw"]],built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"],literal:["true","false","nil"]},l={className:"doctag",begin:"@[A-Za-z]+"},c={begin:"#<",end:">"},d=[t.COMMENT("#","$",{contains:[l]}),t.COMMENT("^=begin","^=end",{contains:[l],relevance:10}),t.COMMENT("^__END__",t.MATCH_NOTHING_RE)],f={className:"subst",begin:/#\{/,end:/\}/,keywords:o},p={className:"string",contains:[t.BACKSLASH_ESCAPE,f],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{begin:e.concat(/<<[-~]?'?/,e.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)),contains:[t.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[t.BACKSLASH_ESCAPE,f]})]}]},m="[1-9](_?[0-9])*|0",g="[0-9](_?[0-9])*",x={className:"number",relevance:0,variants:[{begin:`\\b(${m})(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{begin:"\\b0(_?[0-7])+r?i?\\b"}]},v={variants:[{match:/\(\)/},{className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0,keywords:o}]},I=[p,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class",4:"title.class.inherited"},keywords:o},{match:[/(include|extend)\s+/,i],scope:{2:"title.class"},keywords:o},{relevance:0,match:[i,/\.new[. (]/],scope:{1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"},{relevance:0,match:r,scope:"title.class"},{match:[/def/,/\s+/,n],scope:{1:"keyword",3:"title.function"},contains:[v]},{begin:t.IDENT_RE+"::"},{className:"symbol",begin:t.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[p,{begin:n}],relevance:0},x,{className:"variable",begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{className:"params",begin:/\|(?!=)/,end:/\|/,excludeBegin:!0,excludeEnd:!0,relevance:0,keywords:o},{begin:"("+t.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{className:"regexp",contains:[t.BACKSLASH_ESCAPE,f],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(c,d),relevance:0}].concat(c,d);f.contains=I,v.contains=I;const P=[{begin:/^\s*=>/,starts:{end:"$",contains:I}},{className:"meta.prompt",begin:"^("+"[>?]>"+"|"+"[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]"+"|"+"(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>"+")(?=[ ])",starts:{end:"$",keywords:o,contains:I}}];return d.unshift(c),{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:o,illegal:/\/\*/,contains:[t.SHEBANG({binary:"ruby"})].concat(P).concat(d).concat(I)}}function W7(t){const e=t.regex,n=/(r#)?/,r=e.concat(n,t.UNDERSCORE_IDENT_RE),i=e.concat(n,t.IDENT_RE),s={className:"title.function.invoke",relevance:0,begin:e.concat(/\b/,/(?!let|for|while|if|else|match\b)/,i,e.lookahead(/\s*\(/))},o="([ui](8|16|32|64|128|size)|f(32|64))?",l=["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","union","unsafe","unsized","use","virtual","where","while","yield"],c=["true","false","Some","None","Ok","Err"],d=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],f=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"];return{name:"Rust",aliases:["rs"],keywords:{$pattern:t.IDENT_RE+"!?",type:f,keyword:l,literal:c,built_in:d},illegal:""},s]}}const V7=t=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:t.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:t.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),G7=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","optgroup","option","p","picture","q","quote","samp","section","select","source","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],K7=["defs","g","marker","mask","pattern","svg","switch","symbol","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feFlood","feGaussianBlur","feImage","feMerge","feMorphology","feOffset","feSpecularLighting","feTile","feTurbulence","linearGradient","radialGradient","stop","circle","ellipse","image","line","path","polygon","polyline","rect","text","use","textPath","tspan","foreignObject","clipPath"],Y7=[...G7,...K7],q7=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"].sort().reverse(),X7=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"].sort().reverse(),Q7=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"].sort().reverse(),Z7=["accent-color","align-content","align-items","align-self","alignment-baseline","all","anchor-name","animation","animation-composition","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-range","animation-range-end","animation-range-start","animation-timeline","animation-timing-function","appearance","aspect-ratio","backdrop-filter","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-position-x","background-position-y","background-repeat","background-size","baseline-shift","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-end-end-radius","border-end-start-radius","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-start-end-radius","border-start-start-radius","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-align","box-decoration-break","box-direction","box-flex","box-flex-group","box-lines","box-ordinal-group","box-orient","box-pack","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","color-scheme","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","contain-intrinsic-block-size","contain-intrinsic-height","contain-intrinsic-inline-size","contain-intrinsic-size","contain-intrinsic-width","container","container-name","container-type","content","content-visibility","counter-increment","counter-reset","counter-set","cue","cue-after","cue-before","cursor","cx","cy","direction","display","dominant-baseline","empty-cells","enable-background","field-sizing","fill","fill-opacity","fill-rule","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flood-color","flood-opacity","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-optical-sizing","font-palette","font-size","font-size-adjust","font-smooth","font-smoothing","font-stretch","font-style","font-synthesis","font-synthesis-position","font-synthesis-small-caps","font-synthesis-style","font-synthesis-weight","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-emoji","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","forced-color-adjust","gap","glyph-orientation-horizontal","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphenate-character","hyphenate-limit-chars","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","initial-letter","initial-letter-align","inline-size","inset","inset-area","inset-block","inset-block-end","inset-block-start","inset-inline","inset-inline-end","inset-inline-start","isolation","justify-content","justify-items","justify-self","kerning","left","letter-spacing","lighting-color","line-break","line-height","line-height-step","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","margin-trim","marker","marker-end","marker-mid","marker-start","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","masonry-auto-flow","math-depth","math-shift","math-style","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","offset","offset-anchor","offset-distance","offset-path","offset-position","offset-rotate","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-anchor","overflow-block","overflow-clip-margin","overflow-inline","overflow-wrap","overflow-x","overflow-y","overlay","overscroll-behavior","overscroll-behavior-block","overscroll-behavior-inline","overscroll-behavior-x","overscroll-behavior-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","paint-order","pause","pause-after","pause-before","perspective","perspective-origin","place-content","place-items","place-self","pointer-events","position","position-anchor","position-visibility","print-color-adjust","quotes","r","resize","rest","rest-after","rest-before","right","rotate","row-gap","ruby-align","ruby-position","scale","scroll-behavior","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scroll-timeline","scroll-timeline-axis","scroll-timeline-name","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","shape-rendering","speak","speak-as","src","stop-color","stop-opacity","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","table-layout","text-align","text-align-all","text-align-last","text-anchor","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-skip-ink","text-decoration-style","text-decoration-thickness","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-size-adjust","text-transform","text-underline-offset","text-underline-position","text-wrap","text-wrap-mode","text-wrap-style","timeline-scope","top","touch-action","transform","transform-box","transform-origin","transform-style","transition","transition-behavior","transition-delay","transition-duration","transition-property","transition-timing-function","translate","unicode-bidi","user-modify","user-select","vector-effect","vertical-align","view-timeline","view-timeline-axis","view-timeline-inset","view-timeline-name","view-transition-name","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","white-space-collapse","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","x","y","z-index","zoom"].sort().reverse();function J7(t){const e=V7(t),n=Q7,r=X7,i="@[a-z-]+",s="and or not only",l={className:"variable",begin:"(\\$"+"[a-zA-Z-][a-zA-Z0-9_-]*"+")\\b",relevance:0};return{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,e.CSS_NUMBER_MODE,{className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},e.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag",begin:"\\b("+Y7.join("|")+")\\b",relevance:0},{className:"selector-pseudo",begin:":("+r.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+n.join("|")+")"},l,{begin:/\(/,end:/\)/,contains:[e.CSS_NUMBER_MODE]},e.CSS_VARIABLE,{className:"attribute",begin:"\\b("+Z7.join("|")+")\\b"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:/:/,end:/[;}{]/,relevance:0,contains:[e.BLOCK_COMMENT,l,e.HEXCOLOR,e.CSS_NUMBER_MODE,t.QUOTE_STRING_MODE,t.APOS_STRING_MODE,e.IMPORTANT,e.FUNCTION_DISPATCH]},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,keyword:s,attribute:q7.join(" ")},contains:[{begin:i,className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute"},l,t.QUOTE_STRING_MODE,t.APOS_STRING_MODE,e.HEXCOLOR,e.CSS_NUMBER_MODE]},e.FUNCTION_DISPATCH]}}function ej(t){return{name:"Shell Session",aliases:["console","shellsession"],contains:[{className:"meta.prompt",begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,subLanguage:"bash"}}]}}function tj(t){const e=t.regex,n=t.COMMENT("--","$"),r={scope:"string",variants:[{begin:/'/,end:/'/,contains:[{match:/''/}]}]},i={begin:/"/,end:/"/,contains:[{match:/""/}]},s=["true","false","unknown"],o=["double precision","large object","with timezone","without timezone"],l=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],c=["add","asc","collation","desc","final","first","last","view"],d=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year"],f=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],p=["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"],m=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],g=f,x=[...d,...c].filter(F=>!f.includes(F)),v={scope:"variable",match:/@[a-z0-9][a-z0-9_]*/},S={scope:"operator",match:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0},C={match:e.concat(/\b/,e.either(...g),/\s*\(/),relevance:0,keywords:{built_in:g}};function A(F){return e.concat(/\b/,e.either(...F.map(I=>I.replace(/\s+/,"\\s+"))),/\b/)}const k={scope:"keyword",match:A(m),relevance:0};function M(F,{exceptions:I,when:D}={}){const G=D;return I=I||[],F.map(X=>X.match(/\|\d+$/)||I.includes(X)?X:G(X)?`${X}|0`:X)}return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{$pattern:/\b[\w\.]+/,keyword:M(x,{when:F=>F.length<3}),literal:s,type:l,built_in:p},contains:[{scope:"type",match:A(o)},k,C,v,r,i,t.C_NUMBER_MODE,t.C_BLOCK_COMMENT_MODE,n,S]}}function lN(t){return t?typeof t=="string"?t:t.source:null}function Ou(t){return zt("(?=",t,")")}function zt(...t){return t.map(n=>lN(n)).join("")}function nj(t){const e=t[t.length-1];return typeof e=="object"&&e.constructor===Object?(t.splice(t.length-1,1),e):{}}function br(...t){return"("+(nj(t).capture?"":"?:")+t.map(r=>lN(r)).join("|")+")"}const s1=t=>zt(/\b/,t,/\w$/.test(t)?/\b/:/\B/),rj=["Protocol","Type"].map(s1),uT=["init","self"].map(s1),ij=["Any","Self"],Jg=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","package","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],cT=["false","nil","true"],sj=["assignment","associativity","higherThan","left","lowerThan","none","right"],oj=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],dT=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],uN=br(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),cN=br(uN,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),e0=zt(uN,cN,"*"),dN=br(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),fh=br(dN,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Bi=zt(dN,fh,"*"),gf=zt(/[A-Z]/,fh,"*"),aj=["attached","autoclosure",zt(/convention\(/,br("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",zt(/objc\(/,Bi,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],lj=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"];function uj(t){const e={match:/\s+/,relevance:0},n=t.COMMENT("/\\*","\\*/",{contains:["self"]}),r=[t.C_LINE_COMMENT_MODE,n],i={match:[/\./,br(...rj,...uT)],className:{2:"keyword"}},s={match:zt(/\./,br(...Jg)),relevance:0},o=Jg.filter(nt=>typeof nt=="string").concat(["_|0"]),l=Jg.filter(nt=>typeof nt!="string").concat(ij).map(s1),c={variants:[{className:"keyword",match:br(...l,...uT)}]},d={$pattern:br(/\b\w+/,/#\w+/),keyword:o.concat(oj),literal:cT},f=[i,s,c],p={match:zt(/\./,br(...dT)),relevance:0},m={className:"built_in",match:zt(/\b/,br(...dT),/(?=\()/)},g=[p,m],x={match:/->/,relevance:0},v={className:"operator",relevance:0,variants:[{match:e0},{match:`\\.(\\.|${cN})+`}]},S=[x,v],C="([0-9]_*)+",A="([0-9a-fA-F]_*)+",k={className:"number",relevance:0,variants:[{match:`\\b(${C})(\\.(${C}))?([eE][+-]?(${C}))?\\b`},{match:`\\b0x(${A})(\\.(${A}))?([pP][+-]?(${C}))?\\b`},{match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},M=(nt="")=>({className:"subst",variants:[{match:zt(/\\/,nt,/[0\\tnr"']/)},{match:zt(/\\/,nt,/u\{[0-9a-fA-F]{1,8}\}/)}]}),F=(nt="")=>({className:"subst",match:zt(/\\/,nt,/[\t ]*(?:[\r\n]|\r\n)/)}),I=(nt="")=>({className:"subst",label:"interpol",begin:zt(/\\/,nt,/\(/),end:/\)/}),D=(nt="")=>({begin:zt(nt,/"""/),end:zt(/"""/,nt),contains:[M(nt),F(nt),I(nt)]}),G=(nt="")=>({begin:zt(nt,/"/),end:zt(/"/,nt),contains:[M(nt),I(nt)]}),X={className:"string",variants:[D(),D("#"),D("##"),D("###"),G(),G("#"),G("##"),G("###")]},P=[t.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0,contains:[t.BACKSLASH_ESCAPE]}],Y={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//,contains:P},z=nt=>{const Yt=zt(nt,/\//),Ct=zt(/\//,nt);return{begin:Yt,end:Ct,contains:[...P,{scope:"comment",begin:`#(?!.*${Ct})`,end:/$/}]}},ie={scope:"regexp",variants:[z("###"),z("##"),z("#"),Y]},Z={match:zt(/`/,Bi,/`/)},ee={className:"variable",match:/\$\d+/},ae={className:"variable",match:`\\$${fh}+`},de=[Z,ee,ae],j={match:/(@|#(un)?)available/,scope:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:lj,contains:[...S,k,X]}]}},W={scope:"keyword",match:zt(/@/,br(...aj),Ou(br(/\(/,/\s+/)))},O={scope:"meta",match:zt(/@/,Bi)},U=[j,W,O],Q={match:Ou(/\b[A-Z]/),relevance:0,contains:[{className:"type",match:zt(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,fh,"+")},{className:"type",match:gf,relevance:0},{match:/[?!]+/,relevance:0},{match:/\.\.\./,relevance:0},{match:zt(/\s+&\s+/,Ou(gf)),relevance:0}]},R={begin://,keywords:d,contains:[...r,...f,...U,x,Q]};Q.contains.push(R);const oe={match:zt(Bi,/\s*:/),keywords:"_|0",relevance:0},pe={begin:/\(/,end:/\)/,relevance:0,keywords:d,contains:["self",oe,...r,ie,...f,...g,...S,k,X,...de,...U,Q]},ue={begin://,keywords:"repeat each",contains:[...r,Q]},J={begin:br(Ou(zt(Bi,/\s*:/)),Ou(zt(Bi,/\s+/,Bi,/\s*:/))),end:/:/,relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:Bi}]},he={begin:/\(/,end:/\)/,keywords:d,contains:[J,...r,...f,...S,k,X,...U,Q,pe],endsParent:!0,illegal:/["']/},_e={match:[/(func|macro)/,/\s+/,br(Z.match,Bi,e0)],className:{1:"keyword",3:"title.function"},contains:[ue,he,e],illegal:[/\[/,/%/]},ke={match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"},contains:[ue,he,e],illegal:/\[|%/},Ve={match:[/operator/,/\s+/,e0],className:{1:"keyword",3:"title"}},ot={begin:[/precedencegroup/,/\s+/,gf],className:{1:"keyword",3:"title"},contains:[Q],keywords:[...sj,...cT],end:/}/},qe={match:[/class\b/,/\s+/,/func\b/,/\s+/,/\b[A-Za-z_][A-Za-z0-9_]*\b/],scope:{1:"keyword",3:"keyword",5:"title.function"}},kt={match:[/class\b/,/\s+/,/var\b/],scope:{1:"keyword",3:"keyword"}},fn={begin:[/(struct|protocol|class|extension|enum|actor)/,/\s+/,Bi,/\s*/],beginScope:{1:"keyword",3:"title.class"},keywords:d,contains:[ue,...f,{begin:/:/,end:/\{/,keywords:d,contains:[{scope:"title.class.inherited",match:gf},...f],relevance:0}]};for(const nt of X.variants){const Yt=nt.contains.find(Pn=>Pn.label==="interpol");Yt.keywords=d;const Ct=[...f,...g,...S,k,X,...de];Yt.contains=[...Ct,{begin:/\(/,end:/\)/,contains:["self",...Ct]}]}return{name:"Swift",keywords:d,contains:[...r,_e,ke,qe,kt,fn,Ve,ot,{beginKeywords:"import",end:/$/,contains:[...r],relevance:0},ie,...f,...g,...S,k,X,...de,...U,Q,pe]}}const hh="[A-Za-z$_][0-9A-Za-z$_]*",fN=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],hN=["true","false","null","undefined","NaN","Infinity"],pN=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],mN=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],gN=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],bN=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],EN=[].concat(gN,pN,mN);function cj(t){const e=t.regex,n=(j,{after:W})=>{const O="",end:""},s=/<[A-Za-z0-9\\._:-]+\s*\/>/,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(j,W)=>{const O=j[0].length+j.index,U=j.input[O];if(U==="<"||U===","){W.ignoreMatch();return}U===">"&&(n(j,{after:O})||W.ignoreMatch());let Q;const R=j.input.substring(O);if(Q=R.match(/^\s*=/)){W.ignoreMatch();return}if((Q=R.match(/^\s+extends\s+/))&&Q.index===0){W.ignoreMatch();return}}},l={$pattern:hh,keyword:fN,literal:hN,built_in:EN,"variable.language":bN},c="[0-9](_?[0-9])*",d=`\\.(${c})`,f="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",p={className:"number",variants:[{begin:`(\\b(${f})((${d})|\\.)?|(${d}))[eE][+-]?(${c})\\b`},{begin:`\\b(${f})\\b((${d})\\b|\\.)?|(${d})\\b`},{begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{begin:"\\b0[0-7]+n?\\b"}],relevance:0},m={className:"subst",begin:"\\$\\{",end:"\\}",keywords:l,contains:[]},g={begin:".?html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,m],subLanguage:"xml"}},x={begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,m],subLanguage:"css"}},v={begin:".?gql`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,m],subLanguage:"graphql"}},S={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,m]},A={className:"comment",variants:[t.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:"variable",begin:r+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]},k=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,g,x,v,S,{match:/\$\d+/},p];m.contains=k.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(k)});const M=[].concat(A,m.contains),F=M.concat([{begin:/(\s*)\(/,end:/\)/,keywords:l,contains:["self"].concat(M)}]),I={className:"params",begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:F},D={variants:[{match:[/class/,/\s+/,r,/\s+/,/extends/,/\s+/,e.concat(r,"(",e.concat(/\./,r),")*")],scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{match:[/class/,/\s+/,r],scope:{1:"keyword",3:"title.class"}}]},G={relevance:0,match:e.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),className:"title.class",keywords:{_:[...pN,...mN]}},X={label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},P={variants:[{match:[/function/,/\s+/,r,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:"keyword",3:"title.function"},label:"func.def",contains:[I],illegal:/%/},Y={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"};function z(j){return e.concat("(?!",j.join("|"),")")}const ie={match:e.concat(/\b/,z([...gN,"super","import"].map(j=>`${j}\\s*\\(`)),r,e.lookahead(/\s*\(/)),className:"title.function",relevance:0},Z={begin:e.concat(/\./,e.lookahead(e.concat(r,/(?![0-9A-Za-z$_(])/))),end:r,excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},ee={match:[/get|set/,/\s+/,r,/(?=\()/],className:{1:"keyword",3:"title.function"},contains:[{begin:/\(\)/},I]},ae="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",de={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,e.lookahead(ae)],keywords:"async",className:{1:"keyword",3:"title.function"},contains:[I]};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:F,CLASS_REFERENCE:G},illegal:/#(?![$_A-z])/,contains:[t.SHEBANG({label:"shebang",binary:"node",relevance:5}),X,t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,g,x,v,S,A,{match:/\$\d+/},p,G,{scope:"attr",match:r+e.lookahead(":"),relevance:0},de,{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[A,t.REGEXP_MODE,{className:"function",begin:ae,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:F}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:i.begin,end:i.end},{match:s},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,contains:["self"]}]}]},P,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+t.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[I,t.inherit(t.TITLE_MODE,{begin:r,className:"title.function"})]},{match:/\.\.\./,relevance:0},Z,{match:"\\$"+r,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[I]},ie,Y,D,ee,{match:/\$[(.]/}]}}function dj(t){const e=t.regex,n=cj(t),r=hh,i=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],s={begin:[/namespace/,/\s+/,t.IDENT_RE],beginScope:{1:"keyword",3:"title.class"}},o={beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:{keyword:"interface extends",built_in:i},contains:[n.exports.CLASS_REFERENCE]},l={className:"meta",relevance:10,begin:/^\s*['"]use strict['"]/},c=["type","interface","public","private","protected","implements","declare","abstract","readonly","enum","override","satisfies"],d={$pattern:hh,keyword:fN.concat(c),literal:hN,built_in:EN.concat(i),"variable.language":bN},f={className:"meta",begin:"@"+r},p=(v,S,C)=>{const A=v.contains.findIndex(k=>k.label===S);if(A===-1)throw new Error("can not find mode to replace");v.contains.splice(A,1,C)};Object.assign(n.keywords,d),n.exports.PARAMS_CONTAINS.push(f);const m=n.contains.find(v=>v.scope==="attr"),g=Object.assign({},m,{match:e.concat(r,e.lookahead(/\s*\?:/))});n.exports.PARAMS_CONTAINS.push([n.exports.CLASS_REFERENCE,m,g]),n.contains=n.contains.concat([f,s,o,g]),p(n,"shebang",t.SHEBANG()),p(n,"use_strict",l);const x=n.contains.find(v=>v.label==="func.def");return x.relevance=0,Object.assign(n,{name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),n}function fj(t){const e=t.regex,n={className:"string",begin:/"(""|[^/n])"C\b/},r={className:"string",begin:/"/,end:/"/,illegal:/\n/,contains:[{begin:/""/}]},i=/\d{1,2}\/\d{1,2}\/\d{4}/,s=/\d{4}-\d{1,2}-\d{1,2}/,o=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,l=/\d{1,2}(:\d{1,2}){1,2}/,c={className:"literal",variants:[{begin:e.concat(/# */,e.either(s,i),/ *#/)},{begin:e.concat(/# */,l,/ *#/)},{begin:e.concat(/# */,o,/ *#/)},{begin:e.concat(/# */,e.either(s,i),/ +/,e.either(o,l),/ *#/)}]},d={className:"number",relevance:0,variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},f={className:"label",begin:/^\w+:/},p=t.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}]}),m=t.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]});return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0,classNameAliases:{label:"symbol"},keywords:{keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield",built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort",type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort",literal:"true false nothing"},illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[n,r,c,d,f,p,m,{className:"meta",begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/,end:/$/,keywords:{keyword:"const disable else elseif enable end externalsource if region then"},contains:[m]}]}}function hj(t){t.regex;const e=t.COMMENT(/\(;/,/;\)/);e.contains.push("self");const n=t.COMMENT(/;;/,/$/),r=["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"],i={begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword",3:"title.function"}},s={className:"variable",begin:/\$[\w_]+/},o={match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},l={className:"number",relevance:0,match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/},c={match:/(i32|i64|f32|f64)(?!\.)/,className:"type"},d={className:"keyword",match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/};return{name:"WebAssembly",keywords:{$pattern:/[\w.]+/,keyword:r},contains:[n,e,{match:[/(?:offset|align)/,/\s*/,/=/],className:{1:"keyword",3:"operator"}},s,o,i,t.QUOTE_STRING_MODE,c,d,l]}}function pj(t){const e=t.regex,n=e.concat(/[\p{L}_]/u,e.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),r=/[\p{L}0-9._:-]+/u,i={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},s={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},o=t.inherit(s,{begin:/\(/,end:/\)/}),l=t.inherit(t.APOS_STRING_MODE,{className:"string"}),c=t.inherit(t.QUOTE_STRING_MODE,{className:"string"}),d={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[s,c,l,o,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[s,o,c,l]}]}]},t.COMMENT(//,{relevance:10}),{begin://,relevance:10},i,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[c]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[d],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[d],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:e.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:d}]},{className:"tag",begin:e.concat(/<\//,e.lookahead(e.concat(n,/>/))),contains:[{className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}function mj(t){const e="true false yes no null",n="[\\w#;/?:@&=+$,.~*'()[\\]]+",r={className:"attr",variants:[{begin:/[\w*@][\w*@ :()\./-]*:(?=[ \t]|$)/},{begin:/"[\w*@][\w*@ :()\./-]*":(?=[ \t]|$)/},{begin:/'[\w*@][\w*@ :()\./-]*':(?=[ \t]|$)/}]},i={className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]},s={className:"string",relevance:0,begin:/'/,end:/'/,contains:[{match:/''/,scope:"char.escape",relevance:0}]},o={className:"string",relevance:0,variants:[{begin:/"/,end:/"/},{begin:/\S+/}],contains:[t.BACKSLASH_ESCAPE,i]},l=t.inherit(o,{variants:[{begin:/'/,end:/'/,contains:[{begin:/''/,relevance:0}]},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),m={className:"number",begin:"\\b"+"[0-9]{4}(-[0-9][0-9]){0,2}"+"([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?"+"(\\.[0-9]*)?"+"([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?"+"\\b"},g={end:",",endsWithParent:!0,excludeEnd:!0,keywords:e,relevance:0},x={begin:/\{/,end:/\}/,contains:[g],illegal:"\\n",relevance:0},v={begin:"\\[",end:"\\]",contains:[g],illegal:"\\n",relevance:0},S=[r,{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+n},{className:"type",begin:"!<"+n+">"},{className:"type",begin:"!"+n},{className:"type",begin:"!!"+n},{className:"meta",begin:"&"+t.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+t.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},t.HASH_COMMENT_MODE,{beginKeywords:e,keywords:{literal:e}},m,{className:"number",begin:t.C_NUMBER_RE+"\\b",relevance:0},x,v,s,o],C=[...S];return C.pop(),C.push(l),g.contains=C,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:S}}const gj={arduino:Qz,bash:Zz,c:Jz,cpp:e7,csharp:t7,css:c7,diff:d7,go:f7,graphql:h7,ini:p7,java:m7,javascript:x7,json:v7,kotlin:T7,less:I7,lua:O7,makefile:M7,markdown:D7,objectivec:L7,perl:P7,php:F7,"php-template":B7,plaintext:U7,python:H7,"python-repl":z7,r:j7,ruby:$7,rust:W7,scss:J7,shell:ej,sql:tj,swift:uj,typescript:dj,vbnet:fj,wasm:hj,xml:pj,yaml:mj};var t0,fT;function bj(){if(fT)return t0;fT=1;function t(V){return V instanceof Map?V.clear=V.delete=V.set=function(){throw new Error("map is read-only")}:V instanceof Set&&(V.add=V.clear=V.delete=function(){throw new Error("set is read-only")}),Object.freeze(V),Object.getOwnPropertyNames(V).forEach(te=>{const me=V[te],De=typeof me;(De==="object"||De==="function")&&!Object.isFrozen(me)&&t(me)}),V}class e{constructor(te){te.data===void 0&&(te.data={}),this.data=te.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function n(V){return V.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(V,...te){const me=Object.create(null);for(const De in V)me[De]=V[De];return te.forEach(function(De){for(const wt in De)me[wt]=De[wt]}),me}const i="",s=V=>!!V.scope,o=(V,{prefix:te})=>{if(V.startsWith("language:"))return V.replace("language:","language-");if(V.includes(".")){const me=V.split(".");return[`${te}${me.shift()}`,...me.map((De,wt)=>`${De}${"_".repeat(wt+1)}`)].join(" ")}return`${te}${V}`};class l{constructor(te,me){this.buffer="",this.classPrefix=me.classPrefix,te.walk(this)}addText(te){this.buffer+=n(te)}openNode(te){if(!s(te))return;const me=o(te.scope,{prefix:this.classPrefix});this.span(me)}closeNode(te){s(te)&&(this.buffer+=i)}value(){return this.buffer}span(te){this.buffer+=``}}const c=(V={})=>{const te={children:[]};return Object.assign(te,V),te};class d{constructor(){this.rootNode=c(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(te){this.top.children.push(te)}openNode(te){const me=c({scope:te});this.add(me),this.stack.push(me)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(te){return this.constructor._walk(te,this.rootNode)}static _walk(te,me){return typeof me=="string"?te.addText(me):me.children&&(te.openNode(me),me.children.forEach(De=>this._walk(te,De)),te.closeNode(me)),te}static _collapse(te){typeof te!="string"&&te.children&&(te.children.every(me=>typeof me=="string")?te.children=[te.children.join("")]:te.children.forEach(me=>{d._collapse(me)}))}}class f extends d{constructor(te){super(),this.options=te}addText(te){te!==""&&this.add(te)}startScope(te){this.openNode(te)}endScope(){this.closeNode()}__addSublanguage(te,me){const De=te.root;me&&(De.scope=`language:${me}`),this.add(De)}toHTML(){return new l(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function p(V){return V?typeof V=="string"?V:V.source:null}function m(V){return v("(?=",V,")")}function g(V){return v("(?:",V,")*")}function x(V){return v("(?:",V,")?")}function v(...V){return V.map(me=>p(me)).join("")}function S(V){const te=V[V.length-1];return typeof te=="object"&&te.constructor===Object?(V.splice(V.length-1,1),te):{}}function C(...V){return"("+(S(V).capture?"":"?:")+V.map(De=>p(De)).join("|")+")"}function A(V){return new RegExp(V.toString()+"|").exec("").length-1}function k(V,te){const me=V&&V.exec(te);return me&&me.index===0}const M=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function F(V,{joinWith:te}){let me=0;return V.map(De=>{me+=1;const wt=me;let _t=p(De),Oe="";for(;_t.length>0;){const Ie=M.exec(_t);if(!Ie){Oe+=_t;break}Oe+=_t.substring(0,Ie.index),_t=_t.substring(Ie.index+Ie[0].length),Ie[0][0]==="\\"&&Ie[1]?Oe+="\\"+String(Number(Ie[1])+wt):(Oe+=Ie[0],Ie[0]==="("&&me++)}return Oe}).map(De=>`(${De})`).join(te)}const I=/\b\B/,D="[a-zA-Z]\\w*",G="[a-zA-Z_]\\w*",X="\\b\\d+(\\.\\d+)?",P="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",Y="\\b(0b[01]+)",z="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",ie=(V={})=>{const te=/^#![ ]*\//;return V.binary&&(V.begin=v(te,/.*\b/,V.binary,/\b.*/)),r({scope:"meta",begin:te,end:/$/,relevance:0,"on:begin":(me,De)=>{me.index!==0&&De.ignoreMatch()}},V)},Z={begin:"\\\\[\\s\\S]",relevance:0},ee={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[Z]},ae={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[Z]},de={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},j=function(V,te,me={}){const De=r({scope:"comment",begin:V,end:te,contains:[]},me);De.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const wt=C("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return De.contains.push({begin:v(/[ ]+/,"(",wt,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),De},W=j("//","$"),O=j("/\\*","\\*/"),U=j("#","$"),Q={scope:"number",begin:X,relevance:0},R={scope:"number",begin:P,relevance:0},oe={scope:"number",begin:Y,relevance:0},pe={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[Z,{begin:/\[/,end:/\]/,relevance:0,contains:[Z]}]},ue={scope:"title",begin:D,relevance:0},J={scope:"title",begin:G,relevance:0},he={begin:"\\.\\s*"+G,relevance:0};var ke=Object.freeze({__proto__:null,APOS_STRING_MODE:ee,BACKSLASH_ESCAPE:Z,BINARY_NUMBER_MODE:oe,BINARY_NUMBER_RE:Y,COMMENT:j,C_BLOCK_COMMENT_MODE:O,C_LINE_COMMENT_MODE:W,C_NUMBER_MODE:R,C_NUMBER_RE:P,END_SAME_AS_BEGIN:function(V){return Object.assign(V,{"on:begin":(te,me)=>{me.data._beginMatch=te[1]},"on:end":(te,me)=>{me.data._beginMatch!==te[1]&&me.ignoreMatch()}})},HASH_COMMENT_MODE:U,IDENT_RE:D,MATCH_NOTHING_RE:I,METHOD_GUARD:he,NUMBER_MODE:Q,NUMBER_RE:X,PHRASAL_WORDS_MODE:de,QUOTE_STRING_MODE:ae,REGEXP_MODE:pe,RE_STARTERS_RE:z,SHEBANG:ie,TITLE_MODE:ue,UNDERSCORE_IDENT_RE:G,UNDERSCORE_TITLE_MODE:J});function Ve(V,te){V.input[V.index-1]==="."&&te.ignoreMatch()}function ot(V,te){V.className!==void 0&&(V.scope=V.className,delete V.className)}function qe(V,te){te&&V.beginKeywords&&(V.begin="\\b("+V.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",V.__beforeBegin=Ve,V.keywords=V.keywords||V.beginKeywords,delete V.beginKeywords,V.relevance===void 0&&(V.relevance=0))}function kt(V,te){Array.isArray(V.illegal)&&(V.illegal=C(...V.illegal))}function fn(V,te){if(V.match){if(V.begin||V.end)throw new Error("begin & end are not supported with match");V.begin=V.match,delete V.match}}function nt(V,te){V.relevance===void 0&&(V.relevance=1)}const Yt=(V,te)=>{if(!V.beforeMatch)return;if(V.starts)throw new Error("beforeMatch cannot be used with starts");const me=Object.assign({},V);Object.keys(V).forEach(De=>{delete V[De]}),V.keywords=me.keywords,V.begin=v(me.beforeMatch,m(me.begin)),V.starts={relevance:0,contains:[Object.assign(me,{endsParent:!0})]},V.relevance=0,delete me.beforeMatch},Ct=["of","and","for","in","not","or","if","then","parent","list","value"],Pn="keyword";function Fn(V,te,me=Pn){const De=Object.create(null);return typeof V=="string"?wt(me,V.split(" ")):Array.isArray(V)?wt(me,V):Object.keys(V).forEach(function(_t){Object.assign(De,Fn(V[_t],te,_t))}),De;function wt(_t,Oe){te&&(Oe=Oe.map(Ie=>Ie.toLowerCase())),Oe.forEach(function(Ie){const He=Ie.split("|");De[He[0]]=[_t,on(He[0],He[1])]})}}function on(V,te){return te?Number(te):dr(V)?0:1}function dr(V){return Ct.includes(V.toLowerCase())}const Mn={},Qn=V=>{console.error(V)},li=(V,...te)=>{console.log(`WARN: ${V}`,...te)},ce=(V,te)=>{Mn[`${V}/${te}`]||(console.log(`Deprecated as of ${V}. ${te}`),Mn[`${V}/${te}`]=!0)},ye=new Error;function Qe(V,te,{key:me}){let De=0;const wt=V[me],_t={},Oe={};for(let Ie=1;Ie<=te.length;Ie++)Oe[Ie+De]=wt[Ie],_t[Ie+De]=!0,De+=A(te[Ie-1]);V[me]=Oe,V[me]._emit=_t,V[me]._multi=!0}function ut(V){if(Array.isArray(V.begin)){if(V.skip||V.excludeBegin||V.returnBegin)throw Qn("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),ye;if(typeof V.beginScope!="object"||V.beginScope===null)throw Qn("beginScope must be object"),ye;Qe(V,V.begin,{key:"beginScope"}),V.begin=F(V.begin,{joinWith:""})}}function rt(V){if(Array.isArray(V.end)){if(V.skip||V.excludeEnd||V.returnEnd)throw Qn("skip, excludeEnd, returnEnd not compatible with endScope: {}"),ye;if(typeof V.endScope!="object"||V.endScope===null)throw Qn("endScope must be object"),ye;Qe(V,V.end,{key:"endScope"}),V.end=F(V.end,{joinWith:""})}}function an(V){V.scope&&typeof V.scope=="object"&&V.scope!==null&&(V.beginScope=V.scope,delete V.scope)}function Zn(V){an(V),typeof V.beginScope=="string"&&(V.beginScope={_wrap:V.beginScope}),typeof V.endScope=="string"&&(V.endScope={_wrap:V.endScope}),ut(V),rt(V)}function Re(V){function te(Oe,Ie){return new RegExp(p(Oe),"m"+(V.case_insensitive?"i":"")+(V.unicodeRegex?"u":"")+(Ie?"g":""))}class me{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(Ie,He){He.position=this.position++,this.matchIndexes[this.matchAt]=He,this.regexes.push([He,Ie]),this.matchAt+=A(Ie)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const Ie=this.regexes.map(He=>He[1]);this.matcherRe=te(F(Ie,{joinWith:"|"}),!0),this.lastIndex=0}exec(Ie){this.matcherRe.lastIndex=this.lastIndex;const He=this.matcherRe.exec(Ie);if(!He)return null;const Bt=He.findIndex((Ri,Ns)=>Ns>0&&Ri!==void 0),Xt=this.matchIndexes[Bt];return He.splice(0,Bt),Object.assign(He,Xt)}}class De{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(Ie){if(this.multiRegexes[Ie])return this.multiRegexes[Ie];const He=new me;return this.rules.slice(Ie).forEach(([Bt,Xt])=>He.addRule(Bt,Xt)),He.compile(),this.multiRegexes[Ie]=He,He}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(Ie,He){this.rules.push([Ie,He]),He.type==="begin"&&this.count++}exec(Ie){const He=this.getMatcher(this.regexIndex);He.lastIndex=this.lastIndex;let Bt=He.exec(Ie);if(this.resumingScanAtSamePosition()&&!(Bt&&Bt.index===this.lastIndex)){const Xt=this.getMatcher(0);Xt.lastIndex=this.lastIndex+1,Bt=Xt.exec(Ie)}return Bt&&(this.regexIndex+=Bt.position+1,this.regexIndex===this.count&&this.considerAll()),Bt}}function wt(Oe){const Ie=new De;return Oe.contains.forEach(He=>Ie.addRule(He.begin,{rule:He,type:"begin"})),Oe.terminatorEnd&&Ie.addRule(Oe.terminatorEnd,{type:"end"}),Oe.illegal&&Ie.addRule(Oe.illegal,{type:"illegal"}),Ie}function _t(Oe,Ie){const He=Oe;if(Oe.isCompiled)return He;[ot,fn,Zn,Yt].forEach(Xt=>Xt(Oe,Ie)),V.compilerExtensions.forEach(Xt=>Xt(Oe,Ie)),Oe.__beforeBegin=null,[qe,kt,nt].forEach(Xt=>Xt(Oe,Ie)),Oe.isCompiled=!0;let Bt=null;return typeof Oe.keywords=="object"&&Oe.keywords.$pattern&&(Oe.keywords=Object.assign({},Oe.keywords),Bt=Oe.keywords.$pattern,delete Oe.keywords.$pattern),Bt=Bt||/\w+/,Oe.keywords&&(Oe.keywords=Fn(Oe.keywords,V.case_insensitive)),He.keywordPatternRe=te(Bt,!0),Ie&&(Oe.begin||(Oe.begin=/\B|\b/),He.beginRe=te(He.begin),!Oe.end&&!Oe.endsWithParent&&(Oe.end=/\B|\b/),Oe.end&&(He.endRe=te(He.end)),He.terminatorEnd=p(He.end)||"",Oe.endsWithParent&&Ie.terminatorEnd&&(He.terminatorEnd+=(Oe.end?"|":"")+Ie.terminatorEnd)),Oe.illegal&&(He.illegalRe=te(Oe.illegal)),Oe.contains||(Oe.contains=[]),Oe.contains=[].concat(...Oe.contains.map(function(Xt){return Ge(Xt==="self"?Oe:Xt)})),Oe.contains.forEach(function(Xt){_t(Xt,He)}),Oe.starts&&_t(Oe.starts,Ie),He.matcher=wt(He),He}if(V.compilerExtensions||(V.compilerExtensions=[]),V.contains&&V.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return V.classNameAliases=r(V.classNameAliases||{}),_t(V)}function Me(V){return V?V.endsWithParent||Me(V.starts):!1}function Ge(V){return V.variants&&!V.cachedVariants&&(V.cachedVariants=V.variants.map(function(te){return r(V,{variants:null},te)})),V.cachedVariants?V.cachedVariants:Me(V)?r(V,{starts:V.starts?r(V.starts):null}):Object.isFrozen(V)?r(V):V}var Ke="11.11.1";class bt extends Error{constructor(te,me){super(te),this.name="HTMLInjectionError",this.html=me}}const vt=n,jt=r,fr=Symbol("nomatch"),Dt=7,$t=function(V){const te=Object.create(null),me=Object.create(null),De=[];let wt=!0;const _t="Could not find the language '{}', did you forget to load/include a language module?",Oe={disableAutodetect:!0,name:"Plain text",contains:[]};let Ie={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:f};function He(Ce){return Ie.noHighlightRe.test(Ce)}function Bt(Ce){let Ye=Ce.className+" ";Ye+=Ce.parentNode?Ce.parentNode.className:"";const mt=Ie.languageDetectRe.exec(Ye);if(mt){const Tt=ui(mt[1]);return Tt||(li(_t.replace("{}",mt[1])),li("Falling back to no-highlight mode for this block.",Ce)),Tt?mt[1]:"no-highlight"}return Ye.split(/\s+/).find(Tt=>He(Tt)||ui(Tt))}function Xt(Ce,Ye,mt){let Tt="",vn="";typeof Ye=="object"?(Tt=Ce,mt=Ye.ignoreIllegals,vn=Ye.language):(ce("10.7.0","highlight(lang, code, ...args) has been deprecated."),ce("10.7.0",`Please use highlight(code, options) instead. https://github.com/highlightjs/highlight.js/issues/2277`),vn=Ce,Tt=Ye),mt===void 0&&(mt=!0);const pn={code:Tt,language:vn};yo("before:highlight",pn);const Ii=pn.result?pn.result:Ri(pn.language,pn.code,mt);return Ii.code=pn.code,yo("after:highlight",Ii),Ii}function Ri(Ce,Ye,mt,Tt){const vn=Object.create(null);function pn(Ne,Be){return Ne.keywords[Be]}function Ii(){if(!at.keywords){wn.addText(Ut);return}let Ne=0;at.keywordPatternRe.lastIndex=0;let Be=at.keywordPatternRe.exec(Ut),it="";for(;Be;){it+=Ut.substring(Ne,Be.index);const At=Yr.case_insensitive?Be[0].toLowerCase():Be[0],En=pn(at,At);if(En){const[Bn,Mp]=En;if(wn.addText(it),it="",vn[At]=(vn[At]||0)+1,vn[At]<=Dt&&(wo+=Mp),Bn.startsWith("_"))it+=Be[0];else{const Qc=Yr.classNameAliases[Bn]||Bn;pr(Be[0],Qc)}}else it+=Be[0];Ne=at.keywordPatternRe.lastIndex,Be=at.keywordPatternRe.exec(Ut)}it+=Ut.substring(Ne),wn.addText(it)}function pa(){if(Ut==="")return;let Ne=null;if(typeof at.subLanguage=="string"){if(!te[at.subLanguage]){wn.addText(Ut);return}Ne=Ri(at.subLanguage,Ut,!0,Gl[at.subLanguage]),Gl[at.subLanguage]=Ne._top}else Ne=Eo(Ut,at.subLanguage.length?at.subLanguage:null);at.relevance>0&&(wo+=Ne.relevance),wn.__addSublanguage(Ne._emitter,Ne.language)}function hr(){at.subLanguage!=null?pa():Ii(),Ut=""}function pr(Ne,Be){Ne!==""&&(wn.startScope(Be),wn.addText(Ne),wn.endScope())}function xo(Ne,Be){let it=1;const At=Be.length-1;for(;it<=At;){if(!Ne._emit[it]){it++;continue}const En=Yr.classNameAliases[Ne[it]]||Ne[it],Bn=Be[it];En?pr(Bn,En):(Ut=Bn,Ii(),Ut=""),it++}}function Rs(Ne,Be){return Ne.scope&&typeof Ne.scope=="string"&&wn.openNode(Yr.classNameAliases[Ne.scope]||Ne.scope),Ne.beginScope&&(Ne.beginScope._wrap?(pr(Ut,Yr.classNameAliases[Ne.beginScope._wrap]||Ne.beginScope._wrap),Ut=""):Ne.beginScope._multi&&(xo(Ne.beginScope,Be),Ut="")),at=Object.create(Ne,{parent:{value:at}}),at}function vo(Ne,Be,it){let At=k(Ne.endRe,it);if(At){if(Ne["on:end"]){const En=new e(Ne);Ne["on:end"](Be,En),En.isMatchIgnored&&(At=!1)}if(At){for(;Ne.endsParent&&Ne.parent;)Ne=Ne.parent;return Ne}}if(Ne.endsWithParent)return vo(Ne.parent,Be,it)}function Ip(Ne){return at.matcher.regexIndex===0?(Ut+=Ne[0],1):(Ms=!0,0)}function Op(Ne){const Be=Ne[0],it=Ne.rule,At=new e(it),En=[it.__beforeBegin,it["on:begin"]];for(const Bn of En)if(Bn&&(Bn(Ne,At),At.isMatchIgnored))return Ip(Be);return it.skip?Ut+=Be:(it.excludeBegin&&(Ut+=Be),hr(),!it.returnBegin&&!it.excludeBegin&&(Ut=Be)),Rs(it,Ne),it.returnBegin?0:Be.length}function Wl(Ne){const Be=Ne[0],it=Ye.substring(Ne.index),At=vo(at,Ne,it);if(!At)return fr;const En=at;at.endScope&&at.endScope._wrap?(hr(),pr(Be,at.endScope._wrap)):at.endScope&&at.endScope._multi?(hr(),xo(at.endScope,Ne)):En.skip?Ut+=Be:(En.returnEnd||En.excludeEnd||(Ut+=Be),hr(),En.excludeEnd&&(Ut=Be));do at.scope&&wn.closeNode(),!at.skip&&!at.subLanguage&&(wo+=at.relevance),at=at.parent;while(at!==At.parent);return At.starts&&Rs(At.starts,Ne),En.returnEnd?0:Be.length}function Xc(){const Ne=[];for(let Be=at;Be!==Yr;Be=Be.parent)Be.scope&&Ne.unshift(Be.scope);Ne.forEach(Be=>wn.openNode(Be))}let Is={};function Os(Ne,Be){const it=Be&&Be[0];if(Ut+=Ne,it==null)return hr(),0;if(Is.type==="begin"&&Be.type==="end"&&Is.index===Be.index&&it===""){if(Ut+=Ye.slice(Be.index,Be.index+1),!wt){const At=new Error(`0 width match regex (${Ce})`);throw At.languageName=Ce,At.badRule=Is.rule,At}return 1}if(Is=Be,Be.type==="begin")return Op(Be);if(Be.type==="illegal"&&!mt){const At=new Error('Illegal lexeme "'+it+'" for mode "'+(at.scope||"")+'"');throw At.mode=at,At}else if(Be.type==="end"){const At=Wl(Be);if(At!==fr)return At}if(Be.type==="illegal"&&it==="")return Ut+=` `,1;if(To>1e5&&To>Be.index*3)throw new Error("potential infinite loop, way more iterations than matches");return Ut+=it,it.length}const Yr=ui(Ce);if(!Yr)throw Qn(_t.replace("{}",Ce)),new Error('Unknown language: "'+Ce+'"');const Vl=Re(Yr);let It="",at=Tt||Vl;const Gl={},wn=new Ie.__emitter(Ie);Xc();let Ut="",wo=0,Oi=0,To=0,Ms=!1;try{if(Yr.__emitTokens)Yr.__emitTokens(Ye,wn);else{for(at.matcher.considerAll();;){To++,Ms?Ms=!1:at.matcher.considerAll(),at.matcher.lastIndex=Oi;const Ne=at.matcher.exec(Ye);if(!Ne)break;const Be=Ye.substring(Oi,Ne.index),it=Os(Be,Ne);Oi=Ne.index+it}Os(Ye.substring(Oi))}return wn.finalize(),It=wn.toHTML(),{language:Ce,value:It,relevance:wo,illegal:!1,_emitter:wn,_top:at}}catch(Ne){if(Ne.message&&Ne.message.includes("Illegal"))return{language:Ce,value:vt(Ye),illegal:!0,relevance:0,_illegalBy:{message:Ne.message,index:Oi,context:Ye.slice(Oi-100,Oi+100),mode:Ne.mode,resultSoFar:It},_emitter:wn};if(wt)return{language:Ce,value:vt(Ye),illegal:!1,relevance:0,errorRaised:Ne,_emitter:wn,_top:at};throw Ne}}function Ns(Ce){const Ye={value:vt(Ce),illegal:!1,relevance:0,_top:Oe,_emitter:new Ie.__emitter(Ie)};return Ye._emitter.addText(Ce),Ye}function Eo(Ce,Ye){Ye=Ye||Ie.languages||Object.keys(te);const mt=Ns(Ce),Tt=Ye.filter(ui).filter(qc).map(hr=>Ri(hr,Ce,!1));Tt.unshift(mt);const vn=Tt.sort((hr,pr)=>{if(hr.relevance!==pr.relevance)return pr.relevance-hr.relevance;if(hr.language&&pr.language){if(ui(hr.language).supersetOf===pr.language)return 1;if(ui(pr.language).supersetOf===hr.language)return-1}return 0}),[pn,Ii]=vn,pa=pn;return pa.secondBest=Ii,pa}function kp(Ce,Ye,mt){const Tt=Ye&&me[Ye]||mt;Ce.classList.add("hljs"),Ce.classList.add(`language-${Tt}`)}function zl(Ce){let Ye=null;const mt=Bt(Ce);if(He(mt))return;if(yo("before:highlightElement",{el:Ce,language:mt}),Ce.dataset.highlighted){console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",Ce);return}if(Ce.children.length>0&&(Ie.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(Ce)),Ie.throwUnescapedHTML))throw new bt("One of your code blocks includes unescaped HTML.",Ce.innerHTML);Ye=Ce;const Tt=Ye.textContent,vn=mt?Xt(Tt,{language:mt,ignoreIllegals:!0}):Eo(Tt);Ce.innerHTML=vn.value,Ce.dataset.highlighted="yes",kp(Ce,mt,vn.language),Ce.result={language:vn.language,re:vn.relevance,relevance:vn.relevance},vn.secondBest&&(Ce.secondBest={language:vn.secondBest.language,relevance:vn.secondBest.relevance}),yo("after:highlightElement",{el:Ce,result:vn,text:Tt})}function Np(Ce){Ie=jt(Ie,Ce)}const ts=()=>{fa(),ce("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")};function Wc(){fa(),ce("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")}let jl=!1;function fa(){function Ce(){fa()}if(document.readyState==="loading"){jl||window.addEventListener("DOMContentLoaded",Ce,!1),jl=!0;return}document.querySelectorAll(Ie.cssSelector).forEach(zl)}function Vc(Ce,Ye){let mt=null;try{mt=Ye(V)}catch(Tt){if(Qn("Language definition for '{}' could not be registered.".replace("{}",Ce)),wt)Qn(Tt);else throw Tt;mt=Oe}mt.name||(mt.name=Ce),te[Ce]=mt,mt.rawDefinition=Ye.bind(null,V),mt.aliases&&Yc(mt.aliases,{languageName:Ce})}function Gc(Ce){delete te[Ce];for(const Ye of Object.keys(me))me[Ye]===Ce&&delete me[Ye]}function Kc(){return Object.keys(te)}function ui(Ce){return Ce=(Ce||"").toLowerCase(),te[Ce]||te[me[Ce]]}function Yc(Ce,{languageName:Ye}){typeof Ce=="string"&&(Ce=[Ce]),Ce.forEach(mt=>{me[mt.toLowerCase()]=Ye})}function qc(Ce){const Ye=ui(Ce);return Ye&&!Ye.disableAutodetect}function hn(Ce){Ce["before:highlightBlock"]&&!Ce["before:highlightElement"]&&(Ce["before:highlightElement"]=Ye=>{Ce["before:highlightBlock"](Object.assign({block:Ye.el},Ye))}),Ce["after:highlightBlock"]&&!Ce["after:highlightElement"]&&(Ce["after:highlightElement"]=Ye=>{Ce["after:highlightBlock"](Object.assign({block:Ye.el},Ye))})}function Rp(Ce){hn(Ce),De.push(Ce)}function $l(Ce){const Ye=De.indexOf(Ce);Ye!==-1&&De.splice(Ye,1)}function yo(Ce,Ye){const mt=Ce;De.forEach(function(Tt){Tt[mt]&&Tt[mt](Ye)})}function ha(Ce){return ce("10.7.0","highlightBlock will be removed entirely in v12.0"),ce("10.7.0","Please use highlightElement now."),zl(Ce)}Object.assign(V,{highlight:Xt,highlightAuto:Eo,highlightAll:fa,highlightElement:zl,highlightBlock:ha,configure:Np,initHighlighting:ts,initHighlightingOnLoad:Wc,registerLanguage:Vc,unregisterLanguage:Gc,listLanguages:Kc,getLanguage:ui,registerAliases:Yc,autoDetection:qc,inherit:jt,addPlugin:Rp,removePlugin:$l}),V.debugMode=function(){wt=!1},V.safeMode=function(){wt=!0},V.versionString=Ke,V.regex={concat:v,lookahead:m,either:C,optional:x,anyNumberOfTimes:g};for(const Ce in ke)typeof ke[Ce]=="object"&&t(ke[Ce]);return Object.assign(V,ke),V},qt=$t({});return qt.newInstance=()=>$t({}),t0=qt,qt.HighlightJS=qt,qt.default=qt,t0}var Ej=bj();const yj=_s(Ej),hT={},xj="hljs-";function vj(t){const e=yj.newInstance();return t&&s(t),{highlight:n,highlightAuto:r,listLanguages:i,register:s,registerAlias:o,registered:l};function n(c,d,f){const p=f||hT,m=typeof p.prefix=="string"?p.prefix:xj;if(!e.getLanguage(c))throw new Error("Unknown language: `"+c+"` is not registered");e.configure({__emitter:wj,classPrefix:m});const g=e.highlight(d,{ignoreIllegals:!0,language:c});if(g.errorRaised)throw new Error("Could not highlight with `Highlight.js`",{cause:g.errorRaised});const x=g._emitter.root,v=x.data;return v.language=g.language,v.relevance=g.relevance,x}function r(c,d){const p=(d||hT).subset||i();let m=-1,g=0,x;for(;++mg&&(g=S.data.relevance,x=S)}return x||{type:"root",children:[],data:{language:void 0,relevance:g}}}function i(){return e.listLanguages()}function s(c,d){if(typeof c=="string")e.registerLanguage(c,d);else{let f;for(f in c)Object.hasOwn(c,f)&&e.registerLanguage(f,c[f])}}function o(c,d){if(typeof c=="string")e.registerAliases(typeof d=="string"?d:[...d],{languageName:c});else{let f;for(f in c)if(Object.hasOwn(c,f)){const p=c[f];e.registerAliases(typeof p=="string"?p:[...p],{languageName:f})}}}function l(c){return!!e.getLanguage(c)}}class wj{constructor(e){this.options=e,this.root={type:"root",children:[],data:{language:void 0,relevance:0}},this.stack=[this.root]}addText(e){if(e==="")return;const n=this.stack[this.stack.length-1],r=n.children[n.children.length-1];r&&r.type==="text"?r.value+=e:n.children.push({type:"text",value:e})}startScope(e){this.openNode(String(e))}endScope(){this.closeNode()}__addSublanguage(e,n){const r=this.stack[this.stack.length-1],i=e.root.children;n?r.children.push({type:"element",tagName:"span",properties:{className:[n]},children:i}):r.children.push(...i)}openNode(e){const n=this,r=e.split(".").map(function(o,l){return l?o+"_".repeat(l):n.options.classPrefix+o}),i=this.stack[this.stack.length-1],s={type:"element",tagName:"span",properties:{className:r},children:[]};i.children.push(s),this.stack.push(s)}closeNode(){this.stack.pop()}finalize(){}toHTML(){return""}}const Tj={};function Sj(t){const e=t||Tj,n=e.aliases,r=e.detect||!1,i=e.languages||gj,s=e.plainText,o=e.prefix,l=e.subset;let c="hljs";const d=vj(i);if(n&&d.registerAlias(n),o){const f=o.indexOf("-");c=f===-1?o:o.slice(0,f)}return function(f,p){Pc(f,"element",function(m,g,x){if(m.tagName!=="code"||!x||x.type!=="element"||x.tagName!=="pre")return;const v=_j(m);if(v===!1||!v&&!r||v&&s&&s.includes(v))return;Array.isArray(m.properties.className)||(m.properties.className=[]),m.properties.className.includes(c)||m.properties.className.unshift(c);const S=$z(m,{whitespace:"pre"});let C;try{C=v?d.highlight(v,S,{prefix:o}):d.highlightAuto(S,{prefix:o,subset:l})}catch(A){const k=A;if(v&&/Unknown language/.test(k.message)){p.message("Cannot highlight as `"+v+"`, it’s not registered",{ancestors:[x,m],cause:k,place:m.position,ruleId:"missing-language",source:"rehype-highlight"});return}throw k}!v&&C.data&&C.data.language&&m.properties.className.push("language-"+C.data.language),C.children.length>0&&(m.children=C.children)})}}function _j(t){const e=t.properties.className;let n=-1;if(!Array.isArray(e))return;let r;for(;++n-1&&s<=e.length){let o=0;for(;;){let l=n[o];if(l===void 0){const c=gT(e,n[o-1]);l=c===-1?e.length+1:c+1,n[o]=l}if(l>s)return{line:o+1,column:s-(o>0?n[o-1]:0)+1,offset:s};o++}}}function i(s){if(s&&typeof s.line=="number"&&typeof s.column=="number"&&!Number.isNaN(s.line)&&!Number.isNaN(s.column)){for(;n.length1?n[s.line-2]:0)+s.column-1;if(o4&&n.slice(0,4)==="data"&&$j.test(e)){if(e.charAt(4)==="-"){const s=e.slice(5).replace(yT,Kj);r="data"+s.charAt(0).toUpperCase()+s.slice(1)}else{const s=e.slice(4);if(!yT.test(s)){let o=s.replace(Wj,Gj);o.charAt(0)!=="-"&&(o="-"+o),e="data"+o}}i=a1}return new i(r,e)}function Gj(t){return"-"+t.toLowerCase()}function Kj(t){return t.charAt(1).toUpperCase()}const Yj=wN([_N,SN,kN,NN,zj],"html"),RN=wN([_N,SN,kN,NN,jj],"svg"),qj={},Xj={}.hasOwnProperty,IN=Ok("type",{handlers:{root:Zj,element:r$,text:t$,comment:n$,doctype:e$}});function Qj(t,e){const r=(e||qj).space;return IN(t,r==="svg"?RN:Yj)}function Zj(t,e){const n={nodeName:"#document",mode:(t.data||{}).quirksMode?"quirks":"no-quirks",childNodes:[]};return n.childNodes=l1(t.children,n,e),Pl(t,n),n}function Jj(t,e){const n={nodeName:"#document-fragment",childNodes:[]};return n.childNodes=l1(t.children,n,e),Pl(t,n),n}function e$(t){const e={nodeName:"#documentType",name:"html",publicId:"",systemId:"",parentNode:null};return Pl(t,e),e}function t$(t){const e={nodeName:"#text",value:t.value,parentNode:null};return Pl(t,e),e}function n$(t){const e={nodeName:"#comment",data:t.value,parentNode:null};return Pl(t,e),e}function r$(t,e){const n=e;let r=n;t.type==="element"&&t.tagName.toLowerCase()==="svg"&&n.space==="html"&&(r=RN);const i=[];let s;if(t.properties){for(s in t.properties)if(s!=="children"&&Xj.call(t.properties,s)){const c=i$(r,s,t.properties[s]);c&&i.push(c)}}const o=r.space,l={nodeName:t.tagName,tagName:t.tagName,attrs:i,namespaceURI:Ho[o],childNodes:[],parentNode:null};return l.childNodes=l1(t.children,l,r),Pl(t,l),t.tagName==="template"&&t.content&&(l.content=Jj(t.content,r)),l}function i$(t,e,n){const r=Vj(t,e);if(n===!1||n===null||n===void 0||typeof n=="number"&&Number.isNaN(n)||!n&&r.boolean)return;Array.isArray(n)&&(n=r.commaSeparated?GA(n):ek(n));const i={name:r.attribute,value:n===!0?"":String(n)};if(r.space&&r.space!=="html"&&r.space!=="svg"){const s=i.name.indexOf(":");s<0?i.prefix="":(i.name=i.name.slice(s+1),i.prefix=r.attribute.slice(0,s)),i.namespace=Ho[r.space]}return i}function l1(t,e,n){let r=-1;const i=[];if(t)for(;++r=55296&&t<=57343}function a$(t){return t>=56320&&t<=57343}function l$(t,e){return(t-55296)*1024+9216+e}function MN(t){return t!==32&&t!==10&&t!==13&&t!==9&&t!==12&&t>=1&&t<=31||t>=127&&t<=159}function DN(t){return t>=64976&&t<=65007||o$.has(t)}var fe;(function(t){t.controlCharacterInInputStream="control-character-in-input-stream",t.noncharacterInInputStream="noncharacter-in-input-stream",t.surrogateInInputStream="surrogate-in-input-stream",t.nonVoidHtmlElementStartTagWithTrailingSolidus="non-void-html-element-start-tag-with-trailing-solidus",t.endTagWithAttributes="end-tag-with-attributes",t.endTagWithTrailingSolidus="end-tag-with-trailing-solidus",t.unexpectedSolidusInTag="unexpected-solidus-in-tag",t.unexpectedNullCharacter="unexpected-null-character",t.unexpectedQuestionMarkInsteadOfTagName="unexpected-question-mark-instead-of-tag-name",t.invalidFirstCharacterOfTagName="invalid-first-character-of-tag-name",t.unexpectedEqualsSignBeforeAttributeName="unexpected-equals-sign-before-attribute-name",t.missingEndTagName="missing-end-tag-name",t.unexpectedCharacterInAttributeName="unexpected-character-in-attribute-name",t.unknownNamedCharacterReference="unknown-named-character-reference",t.missingSemicolonAfterCharacterReference="missing-semicolon-after-character-reference",t.unexpectedCharacterAfterDoctypeSystemIdentifier="unexpected-character-after-doctype-system-identifier",t.unexpectedCharacterInUnquotedAttributeValue="unexpected-character-in-unquoted-attribute-value",t.eofBeforeTagName="eof-before-tag-name",t.eofInTag="eof-in-tag",t.missingAttributeValue="missing-attribute-value",t.missingWhitespaceBetweenAttributes="missing-whitespace-between-attributes",t.missingWhitespaceAfterDoctypePublicKeyword="missing-whitespace-after-doctype-public-keyword",t.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers="missing-whitespace-between-doctype-public-and-system-identifiers",t.missingWhitespaceAfterDoctypeSystemKeyword="missing-whitespace-after-doctype-system-keyword",t.missingQuoteBeforeDoctypePublicIdentifier="missing-quote-before-doctype-public-identifier",t.missingQuoteBeforeDoctypeSystemIdentifier="missing-quote-before-doctype-system-identifier",t.missingDoctypePublicIdentifier="missing-doctype-public-identifier",t.missingDoctypeSystemIdentifier="missing-doctype-system-identifier",t.abruptDoctypePublicIdentifier="abrupt-doctype-public-identifier",t.abruptDoctypeSystemIdentifier="abrupt-doctype-system-identifier",t.cdataInHtmlContent="cdata-in-html-content",t.incorrectlyOpenedComment="incorrectly-opened-comment",t.eofInScriptHtmlCommentLikeText="eof-in-script-html-comment-like-text",t.eofInDoctype="eof-in-doctype",t.nestedComment="nested-comment",t.abruptClosingOfEmptyComment="abrupt-closing-of-empty-comment",t.eofInComment="eof-in-comment",t.incorrectlyClosedComment="incorrectly-closed-comment",t.eofInCdata="eof-in-cdata",t.absenceOfDigitsInNumericCharacterReference="absence-of-digits-in-numeric-character-reference",t.nullCharacterReference="null-character-reference",t.surrogateCharacterReference="surrogate-character-reference",t.characterReferenceOutsideUnicodeRange="character-reference-outside-unicode-range",t.controlCharacterReference="control-character-reference",t.noncharacterCharacterReference="noncharacter-character-reference",t.missingWhitespaceBeforeDoctypeName="missing-whitespace-before-doctype-name",t.missingDoctypeName="missing-doctype-name",t.invalidCharacterSequenceAfterDoctypeName="invalid-character-sequence-after-doctype-name",t.duplicateAttribute="duplicate-attribute",t.nonConformingDoctype="non-conforming-doctype",t.missingDoctype="missing-doctype",t.misplacedDoctype="misplaced-doctype",t.endTagWithoutMatchingOpenElement="end-tag-without-matching-open-element",t.closingOfElementWithOpenChildElements="closing-of-element-with-open-child-elements",t.disallowedContentInNoscriptInHead="disallowed-content-in-noscript-in-head",t.openElementsLeftAfterEof="open-elements-left-after-eof",t.abandonedHeadElementChild="abandoned-head-element-child",t.misplacedStartTagForHeadElement="misplaced-start-tag-for-head-element",t.nestedNoscriptInHead="nested-noscript-in-head",t.eofInElementThatCanContainOnlyText="eof-in-element-that-can-contain-only-text"})(fe||(fe={}));const u$=65536;class c${constructor(e){this.handler=e,this.html="",this.pos=-1,this.lastGapPos=-2,this.gapStack=[],this.skipNextNewLine=!1,this.lastChunkWritten=!1,this.endOfChunkHit=!1,this.bufferWaterline=u$,this.isEol=!1,this.lineStartPos=0,this.droppedBufferSize=0,this.line=1,this.lastErrOffset=-1}get col(){return this.pos-this.lineStartPos+ +(this.lastGapPos!==this.pos)}get offset(){return this.droppedBufferSize+this.pos}getError(e,n){const{line:r,col:i,offset:s}=this,o=i+n,l=s+n;return{code:e,startLine:r,endLine:r,startCol:o,endCol:o,startOffset:l,endOffset:l}}_err(e){this.handler.onParseError&&this.lastErrOffset!==this.offset&&(this.lastErrOffset=this.offset,this.handler.onParseError(this.getError(e,0)))}_addGap(){this.gapStack.push(this.lastGapPos),this.lastGapPos=this.pos}_processSurrogate(e){if(this.pos!==this.html.length-1){const n=this.html.charCodeAt(this.pos+1);if(a$(n))return this.pos++,this._addGap(),l$(e,n)}else if(!this.lastChunkWritten)return this.endOfChunkHit=!0,L.EOF;return this._err(fe.surrogateInInputStream),e}willDropParsedChunk(){return this.pos>this.bufferWaterline}dropParsedChunk(){this.willDropParsedChunk()&&(this.html=this.html.substring(this.pos),this.lineStartPos-=this.pos,this.droppedBufferSize+=this.pos,this.pos=0,this.lastGapPos=-2,this.gapStack.length=0)}write(e,n){this.html.length>0?this.html+=e:this.html=e,this.endOfChunkHit=!1,this.lastChunkWritten=n}insertHtmlAtCurrentPos(e){this.html=this.html.substring(0,this.pos+1)+e+this.html.substring(this.pos+1),this.endOfChunkHit=!1}startsWith(e,n){if(this.pos+e.length>this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,!1;if(n)return this.html.startsWith(e,this.pos);for(let r=0;r=this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,L.EOF;const r=this.html.charCodeAt(n);return r===L.CARRIAGE_RETURN?L.LINE_FEED:r}advance(){if(this.pos++,this.isEol&&(this.isEol=!1,this.line++,this.lineStartPos=this.pos),this.pos>=this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,L.EOF;let e=this.html.charCodeAt(this.pos);return e===L.CARRIAGE_RETURN?(this.isEol=!0,this.skipNextNewLine=!0,L.LINE_FEED):e===L.LINE_FEED&&(this.isEol=!0,this.skipNextNewLine)?(this.line--,this.skipNextNewLine=!1,this._addGap(),this.advance()):(this.skipNextNewLine=!1,ON(e)&&(e=this._processSurrogate(e)),this.handler.onParseError===null||e>31&&e<127||e===L.LINE_FEED||e===L.CARRIAGE_RETURN||e>159&&e<64976||this._checkForProblematicCharacters(e),e)}_checkForProblematicCharacters(e){MN(e)?this._err(fe.controlCharacterInInputStream):DN(e)&&this._err(fe.noncharacterInInputStream)}retreat(e){for(this.pos-=e;this.pos=0;n--)if(t.attrs[n].name===e)return t.attrs[n].value;return null}const d$=new Uint16Array('ᵁ<Õıʊҝջאٵ۞ޢߖࠏ੊ઑඡ๭༉༦჊ረዡᐕᒝᓃᓟᔥ\0\0\0\0\0\0ᕫᛍᦍᰒᷝ὾⁠↰⊍⏀⏻⑂⠤⤒ⴈ⹈⿎〖㊺㘹㞬㣾㨨㩱㫠㬮ࠀEMabcfglmnoprstu\\bfms„‹•˜¦³¹ÈÏlig耻Æ䃆P耻&䀦cute耻Á䃁reve;䄂Āiyx}rc耻Â䃂;䐐r;쀀𝔄rave耻À䃀pha;䎑acr;䄀d;橓Āgp¡on;䄄f;쀀𝔸plyFunction;恡ing耻Å䃅Ācs¾Ãr;쀀𝒜ign;扔ilde耻Ã䃃ml耻Ä䃄ЀaceforsuåûþėĜĢħĪĀcrêòkslash;或Ŷöø;櫧ed;挆y;䐑ƀcrtąċĔause;戵noullis;愬a;䎒r;쀀𝔅pf;쀀𝔹eve;䋘còēmpeq;扎܀HOacdefhilorsuōőŖƀƞƢƵƷƺǜȕɳɸɾcy;䐧PY耻©䂩ƀcpyŝŢźute;䄆Ā;iŧŨ拒talDifferentialD;慅leys;愭ȀaeioƉƎƔƘron;䄌dil耻Ç䃇rc;䄈nint;戰ot;䄊ĀdnƧƭilla;䂸terDot;䂷òſi;䎧rcleȀDMPTLJNjǑǖot;抙inus;抖lus;投imes;抗oĀcsǢǸkwiseContourIntegral;戲eCurlyĀDQȃȏoubleQuote;思uote;怙ȀlnpuȞȨɇɕonĀ;eȥȦ户;橴ƀgitȯȶȺruent;扡nt;戯ourIntegral;戮ĀfrɌɎ;愂oduct;成nterClockwiseContourIntegral;戳oss;樯cr;쀀𝒞pĀ;Cʄʅ拓ap;才րDJSZacefiosʠʬʰʴʸˋ˗ˡ˦̳ҍĀ;oŹʥtrahd;椑cy;䐂cy;䐅cy;䐏ƀgrsʿ˄ˇger;怡r;憡hv;櫤Āayː˕ron;䄎;䐔lĀ;t˝˞戇a;䎔r;쀀𝔇Āaf˫̧Ācm˰̢riticalȀADGT̖̜̀̆cute;䂴oŴ̋̍;䋙bleAcute;䋝rave;䁠ilde;䋜ond;拄ferentialD;慆Ѱ̽\0\0\0͔͂\0Ѕf;쀀𝔻ƀ;DE͈͉͍䂨ot;惜qual;扐blèCDLRUVͣͲ΂ϏϢϸontourIntegraìȹoɴ͹\0\0ͻ»͉nArrow;懓Āeo·ΤftƀARTΐΖΡrrow;懐ightArrow;懔eåˊngĀLRΫτeftĀARγιrrow;柸ightArrow;柺ightArrow;柹ightĀATϘϞrrow;懒ee;抨pɁϩ\0\0ϯrrow;懑ownArrow;懕erticalBar;戥ǹABLRTaВЪаўѿͼrrowƀ;BUНОТ憓ar;椓pArrow;懵reve;䌑eft˒к\0ц\0ѐightVector;楐eeVector;楞ectorĀ;Bљњ憽ar;楖ightǔѧ\0ѱeeVector;楟ectorĀ;BѺѻ懁ar;楗eeĀ;A҆҇护rrow;憧ĀctҒҗr;쀀𝒟rok;䄐ࠀNTacdfglmopqstuxҽӀӄӋӞӢӧӮӵԡԯԶՒ՝ՠեG;䅊H耻Ð䃐cute耻É䃉ƀaiyӒӗӜron;䄚rc耻Ê䃊;䐭ot;䄖r;쀀𝔈rave耻È䃈ement;戈ĀapӺӾcr;䄒tyɓԆ\0\0ԒmallSquare;旻erySmallSquare;斫ĀgpԦԪon;䄘f;쀀𝔼silon;䎕uĀaiԼՉlĀ;TՂՃ橵ilde;扂librium;懌Āci՗՚r;愰m;橳a;䎗ml耻Ë䃋Āipժկsts;戃onentialE;慇ʀcfiosօֈ֍ֲ׌y;䐤r;쀀𝔉lledɓ֗\0\0֣mallSquare;旼erySmallSquare;斪Ͱֺ\0ֿ\0\0ׄf;쀀𝔽All;戀riertrf;愱cò׋؀JTabcdfgorstר׬ׯ׺؀ؒؖ؛؝أ٬ٲcy;䐃耻>䀾mmaĀ;d׷׸䎓;䏜reve;䄞ƀeiy؇،ؐdil;䄢rc;䄜;䐓ot;䄠r;쀀𝔊;拙pf;쀀𝔾eater̀EFGLSTصلَٖٛ٦qualĀ;Lؾؿ扥ess;招ullEqual;执reater;檢ess;扷lantEqual;橾ilde;扳cr;쀀𝒢;扫ЀAacfiosuڅڋږڛڞڪھۊRDcy;䐪Āctڐڔek;䋇;䁞irc;䄤r;愌lbertSpace;愋ǰگ\0ڲf;愍izontalLine;攀Āctۃۅòکrok;䄦mpńېۘownHumðįqual;扏܀EJOacdfgmnostuۺ۾܃܇܎ܚܞܡܨ݄ݸދޏޕcy;䐕lig;䄲cy;䐁cute耻Í䃍Āiyܓܘrc耻Î䃎;䐘ot;䄰r;愑rave耻Ì䃌ƀ;apܠܯܿĀcgܴܷr;䄪inaryI;慈lieóϝǴ݉\0ݢĀ;eݍݎ戬Āgrݓݘral;戫section;拂isibleĀCTݬݲomma;恣imes;恢ƀgptݿރވon;䄮f;쀀𝕀a;䎙cr;愐ilde;䄨ǫޚ\0ޞcy;䐆l耻Ï䃏ʀcfosuެ޷޼߂ߐĀiyޱ޵rc;䄴;䐙r;쀀𝔍pf;쀀𝕁ǣ߇\0ߌr;쀀𝒥rcy;䐈kcy;䐄΀HJacfosߤߨ߽߬߱ࠂࠈcy;䐥cy;䐌ppa;䎚Āey߶߻dil;䄶;䐚r;쀀𝔎pf;쀀𝕂cr;쀀𝒦րJTaceflmostࠥࠩࠬࡐࡣ঳সে্਷ੇcy;䐉耻<䀼ʀcmnpr࠷࠼ࡁࡄࡍute;䄹bda;䎛g;柪lacetrf;愒r;憞ƀaeyࡗ࡜ࡡron;䄽dil;䄻;䐛Āfsࡨ॰tԀACDFRTUVarࡾࢩࢱࣦ࣠ࣼयज़ΐ४Ānrࢃ࢏gleBracket;柨rowƀ;BR࢙࢚࢞憐ar;懤ightArrow;懆eiling;挈oǵࢷ\0ࣃbleBracket;柦nǔࣈ\0࣒eeVector;楡ectorĀ;Bࣛࣜ懃ar;楙loor;挊ightĀAV࣯ࣵrrow;憔ector;楎Āerँगeƀ;AVउऊऐ抣rrow;憤ector;楚iangleƀ;BEतथऩ抲ar;槏qual;抴pƀDTVषूौownVector;楑eeVector;楠ectorĀ;Bॖॗ憿ar;楘ectorĀ;B॥०憼ar;楒ightáΜs̀EFGLSTॾঋকঝঢভqualGreater;拚ullEqual;扦reater;扶ess;檡lantEqual;橽ilde;扲r;쀀𝔏Ā;eঽা拘ftarrow;懚idot;䄿ƀnpw৔ਖਛgȀLRlr৞৷ਂਐeftĀAR০৬rrow;柵ightArrow;柷ightArrow;柶eftĀarγਊightáοightáϊf;쀀𝕃erĀLRਢਬeftArrow;憙ightArrow;憘ƀchtਾੀੂòࡌ;憰rok;䅁;扪Ѐacefiosuਗ਼੝੠੷੼અઋ઎p;椅y;䐜Ādl੥੯iumSpace;恟lintrf;愳r;쀀𝔐nusPlus;戓pf;쀀𝕄cò੶;䎜ҀJacefostuણધભીଔଙඑ඗ඞcy;䐊cute;䅃ƀaey઴હાron;䅇dil;䅅;䐝ƀgswે૰଎ativeƀMTV૓૟૨ediumSpace;怋hiĀcn૦૘ë૙eryThiî૙tedĀGL૸ଆreaterGreateòٳessLesóੈLine;䀊r;쀀𝔑ȀBnptଢନଷ଺reak;恠BreakingSpace;䂠f;愕ڀ;CDEGHLNPRSTV୕ୖ୪୼஡௫ఄ౞಄ದ೘ൡඅ櫬Āou୛୤ngruent;扢pCap;扭oubleVerticalBar;戦ƀlqxஃஊ஛ement;戉ualĀ;Tஒஓ扠ilde;쀀≂̸ists;戄reater΀;EFGLSTஶஷ஽௉௓௘௥扯qual;扱ullEqual;쀀≧̸reater;쀀≫̸ess;批lantEqual;쀀⩾̸ilde;扵umpń௲௽ownHump;쀀≎̸qual;쀀≏̸eĀfsఊధtTriangleƀ;BEచఛడ拪ar;쀀⧏̸qual;括s̀;EGLSTవశ఼ౄోౘ扮qual;扰reater;扸ess;쀀≪̸lantEqual;쀀⩽̸ilde;扴estedĀGL౨౹reaterGreater;쀀⪢̸essLess;쀀⪡̸recedesƀ;ESಒಓಛ技qual;쀀⪯̸lantEqual;拠ĀeiಫಹverseElement;戌ghtTriangleƀ;BEೋೌ೒拫ar;쀀⧐̸qual;拭ĀquೝഌuareSuĀbp೨೹setĀ;E೰ೳ쀀⊏̸qual;拢ersetĀ;Eഃആ쀀⊐̸qual;拣ƀbcpഓതൎsetĀ;Eഛഞ쀀⊂⃒qual;抈ceedsȀ;ESTലള഻െ抁qual;쀀⪰̸lantEqual;拡ilde;쀀≿̸ersetĀ;E൘൛쀀⊃⃒qual;抉ildeȀ;EFT൮൯൵ൿ扁qual;扄ullEqual;扇ilde;扉erticalBar;戤cr;쀀𝒩ilde耻Ñ䃑;䎝܀Eacdfgmoprstuvලෂ෉෕ෛ෠෧෼ขภยา฿ไlig;䅒cute耻Ó䃓Āiy෎ීrc耻Ô䃔;䐞blac;䅐r;쀀𝔒rave耻Ò䃒ƀaei෮ෲ෶cr;䅌ga;䎩cron;䎟pf;쀀𝕆enCurlyĀDQฎบoubleQuote;怜uote;怘;橔Āclวฬr;쀀𝒪ash耻Ø䃘iŬื฼de耻Õ䃕es;樷ml耻Ö䃖erĀBP๋๠Āar๐๓r;怾acĀek๚๜;揞et;掴arenthesis;揜Ҁacfhilors๿ງຊຏຒດຝະ໼rtialD;戂y;䐟r;쀀𝔓i;䎦;䎠usMinus;䂱Āipຢອncareplanåڝf;愙Ȁ;eio຺ູ໠໤檻cedesȀ;EST່້໏໚扺qual;檯lantEqual;扼ilde;找me;怳Ādp໩໮uct;戏ortionĀ;aȥ໹l;戝Āci༁༆r;쀀𝒫;䎨ȀUfos༑༖༛༟OT耻"䀢r;쀀𝔔pf;愚cr;쀀𝒬؀BEacefhiorsu༾གྷཇའཱིྦྷྪྭ႖ႩႴႾarr;椐G耻®䂮ƀcnrཎནབute;䅔g;柫rĀ;tཛྷཝ憠l;椖ƀaeyཧཬཱron;䅘dil;䅖;䐠Ā;vླྀཹ愜erseĀEUྂྙĀlq྇ྎement;戋uilibrium;懋pEquilibrium;楯r»ཹo;䎡ghtЀACDFTUVa࿁࿫࿳ဢဨၛႇϘĀnr࿆࿒gleBracket;柩rowƀ;BL࿜࿝࿡憒ar;懥eftArrow;懄eiling;按oǵ࿹\0စbleBracket;柧nǔည\0နeeVector;楝ectorĀ;Bဝသ懂ar;楕loor;挋Āerိ၃eƀ;AVဵံြ抢rrow;憦ector;楛iangleƀ;BEၐၑၕ抳ar;槐qual;抵pƀDTVၣၮၸownVector;楏eeVector;楜ectorĀ;Bႂႃ憾ar;楔ectorĀ;B႑႒懀ar;楓Āpuႛ႞f;愝ndImplies;楰ightarrow;懛ĀchႹႼr;愛;憱leDelayed;槴ڀHOacfhimoqstuფჱჷჽᄙᄞᅑᅖᅡᅧᆵᆻᆿĀCcჩხHcy;䐩y;䐨FTcy;䐬cute;䅚ʀ;aeiyᄈᄉᄎᄓᄗ檼ron;䅠dil;䅞rc;䅜;䐡r;쀀𝔖ortȀDLRUᄪᄴᄾᅉownArrow»ОeftArrow»࢚ightArrow»࿝pArrow;憑gma;䎣allCircle;战pf;쀀𝕊ɲᅭ\0\0ᅰt;戚areȀ;ISUᅻᅼᆉᆯ斡ntersection;抓uĀbpᆏᆞsetĀ;Eᆗᆘ抏qual;抑ersetĀ;Eᆨᆩ抐qual;抒nion;抔cr;쀀𝒮ar;拆ȀbcmpᇈᇛሉላĀ;sᇍᇎ拐etĀ;Eᇍᇕqual;抆ĀchᇠህeedsȀ;ESTᇭᇮᇴᇿ扻qual;檰lantEqual;扽ilde;承Tháྌ;我ƀ;esሒሓሣ拑rsetĀ;Eሜም抃qual;抇et»ሓրHRSacfhiorsሾቄ቉ቕ቞ቱቶኟዂወዑORN耻Þ䃞ADE;愢ĀHc቎ቒcy;䐋y;䐦Ābuቚቜ;䀉;䎤ƀaeyብቪቯron;䅤dil;䅢;䐢r;쀀𝔗Āeiቻ኉Dzኀ\0ኇefore;戴a;䎘Ācn኎ኘkSpace;쀀  Space;怉ldeȀ;EFTካኬኲኼ戼qual;扃ullEqual;扅ilde;扈pf;쀀𝕋ipleDot;惛Āctዖዛr;쀀𝒯rok;䅦ૡዷጎጚጦ\0ጬጱ\0\0\0\0\0ጸጽ፷ᎅ\0᏿ᐄᐊᐐĀcrዻጁute耻Ú䃚rĀ;oጇገ憟cir;楉rǣጓ\0጖y;䐎ve;䅬Āiyጞጣrc耻Û䃛;䐣blac;䅰r;쀀𝔘rave耻Ù䃙acr;䅪Ādiፁ፩erĀBPፈ፝Āarፍፐr;䁟acĀekፗፙ;揟et;掵arenthesis;揝onĀ;P፰፱拃lus;抎Āgp፻፿on;䅲f;쀀𝕌ЀADETadps᎕ᎮᎸᏄϨᏒᏗᏳrrowƀ;BDᅐᎠᎤar;椒ownArrow;懅ownArrow;憕quilibrium;楮eeĀ;AᏋᏌ报rrow;憥ownáϳerĀLRᏞᏨeftArrow;憖ightArrow;憗iĀ;lᏹᏺ䏒on;䎥ing;䅮cr;쀀𝒰ilde;䅨ml耻Ü䃜ҀDbcdefosvᐧᐬᐰᐳᐾᒅᒊᒐᒖash;披ar;櫫y;䐒ashĀ;lᐻᐼ抩;櫦Āerᑃᑅ;拁ƀbtyᑌᑐᑺar;怖Ā;iᑏᑕcalȀBLSTᑡᑥᑪᑴar;戣ine;䁼eparator;杘ilde;所ThinSpace;怊r;쀀𝔙pf;쀀𝕍cr;쀀𝒱dash;抪ʀcefosᒧᒬᒱᒶᒼirc;䅴dge;拀r;쀀𝔚pf;쀀𝕎cr;쀀𝒲Ȁfiosᓋᓐᓒᓘr;쀀𝔛;䎞pf;쀀𝕏cr;쀀𝒳ҀAIUacfosuᓱᓵᓹᓽᔄᔏᔔᔚᔠcy;䐯cy;䐇cy;䐮cute耻Ý䃝Āiyᔉᔍrc;䅶;䐫r;쀀𝔜pf;쀀𝕐cr;쀀𝒴ml;䅸ЀHacdefosᔵᔹᔿᕋᕏᕝᕠᕤcy;䐖cute;䅹Āayᕄᕉron;䅽;䐗ot;䅻Dzᕔ\0ᕛoWidtè૙a;䎖r;愨pf;愤cr;쀀𝒵௡ᖃᖊᖐ\0ᖰᖶᖿ\0\0\0\0ᗆᗛᗫᙟ᙭\0ᚕ᚛ᚲᚹ\0ᚾcute耻á䃡reve;䄃̀;Ediuyᖜᖝᖡᖣᖨᖭ戾;쀀∾̳;房rc耻â䃢te肻´̆;䐰lig耻æ䃦Ā;r²ᖺ;쀀𝔞rave耻à䃠ĀepᗊᗖĀfpᗏᗔsym;愵èᗓha;䎱ĀapᗟcĀclᗤᗧr;䄁g;樿ɤᗰ\0\0ᘊʀ;adsvᗺᗻᗿᘁᘇ戧nd;橕;橜lope;橘;橚΀;elmrszᘘᘙᘛᘞᘿᙏᙙ戠;榤e»ᘙsdĀ;aᘥᘦ戡ѡᘰᘲᘴᘶᘸᘺᘼᘾ;榨;榩;榪;榫;榬;榭;榮;榯tĀ;vᙅᙆ戟bĀ;dᙌᙍ抾;榝Āptᙔᙗh;戢»¹arr;捼Āgpᙣᙧon;䄅f;쀀𝕒΀;Eaeiop዁ᙻᙽᚂᚄᚇᚊ;橰cir;橯;扊d;手s;䀧roxĀ;e዁ᚒñᚃing耻å䃥ƀctyᚡᚦᚨr;쀀𝒶;䀪mpĀ;e዁ᚯñʈilde耻ã䃣ml耻ä䃤Āciᛂᛈoninôɲnt;樑ࠀNabcdefiklnoprsu᛭ᛱᜰ᜼ᝃᝈ᝸᝽០៦ᠹᡐᜍ᤽᥈ᥰot;櫭Ācrᛶ᜞kȀcepsᜀᜅᜍᜓong;扌psilon;䏶rime;怵imĀ;e᜚᜛戽q;拍Ŷᜢᜦee;抽edĀ;gᜬᜭ挅e»ᜭrkĀ;t፜᜷brk;掶Āoyᜁᝁ;䐱quo;怞ʀcmprtᝓ᝛ᝡᝤᝨausĀ;eĊĉptyv;榰séᜌnoõēƀahwᝯ᝱ᝳ;䎲;愶een;扬r;쀀𝔟g΀costuvwឍឝឳេ៕៛៞ƀaiuបពរðݠrc;旯p»፱ƀdptឤឨឭot;樀lus;樁imes;樂ɱឹ\0\0ើcup;樆ar;昅riangleĀdu៍្own;施p;斳plus;樄eåᑄåᒭarow;植ƀako៭ᠦᠵĀcn៲ᠣkƀlst៺֫᠂ozenge;槫riangleȀ;dlr᠒᠓᠘᠝斴own;斾eft;旂ight;斸k;搣Ʊᠫ\0ᠳƲᠯ\0ᠱ;斒;斑4;斓ck;斈ĀeoᠾᡍĀ;qᡃᡆ쀀=⃥uiv;쀀≡⃥t;挐Ȁptwxᡙᡞᡧᡬf;쀀𝕓Ā;tᏋᡣom»Ꮜtie;拈؀DHUVbdhmptuvᢅᢖᢪᢻᣗᣛᣬ᣿ᤅᤊᤐᤡȀLRlrᢎᢐᢒᢔ;敗;敔;敖;敓ʀ;DUduᢡᢢᢤᢦᢨ敐;敦;敩;敤;敧ȀLRlrᢳᢵᢷᢹ;敝;敚;敜;教΀;HLRhlrᣊᣋᣍᣏᣑᣓᣕ救;敬;散;敠;敫;敢;敟ox;槉ȀLRlrᣤᣦᣨᣪ;敕;敒;攐;攌ʀ;DUduڽ᣷᣹᣻᣽;敥;敨;攬;攴inus;抟lus;択imes;抠ȀLRlrᤙᤛᤝ᤟;敛;敘;攘;攔΀;HLRhlrᤰᤱᤳᤵᤷ᤻᤹攂;敪;敡;敞;攼;攤;攜Āevģ᥂bar耻¦䂦Ȁceioᥑᥖᥚᥠr;쀀𝒷mi;恏mĀ;e᜚᜜lƀ;bhᥨᥩᥫ䁜;槅sub;柈Ŭᥴ᥾lĀ;e᥹᥺怢t»᥺pƀ;Eeįᦅᦇ;檮Ā;qۜۛೡᦧ\0᧨ᨑᨕᨲ\0ᨷᩐ\0\0᪴\0\0᫁\0\0ᬡᬮ᭍᭒\0᯽\0ᰌƀcpr᦭ᦲ᧝ute;䄇̀;abcdsᦿᧀᧄ᧊᧕᧙戩nd;橄rcup;橉Āau᧏᧒p;橋p;橇ot;橀;쀀∩︀Āeo᧢᧥t;恁îړȀaeiu᧰᧻ᨁᨅǰ᧵\0᧸s;橍on;䄍dil耻ç䃧rc;䄉psĀ;sᨌᨍ橌m;橐ot;䄋ƀdmnᨛᨠᨦil肻¸ƭptyv;榲t脀¢;eᨭᨮ䂢räƲr;쀀𝔠ƀceiᨽᩀᩍy;䑇ckĀ;mᩇᩈ朓ark»ᩈ;䏇r΀;Ecefms᩟᩠ᩢᩫ᪤᪪᪮旋;槃ƀ;elᩩᩪᩭ䋆q;扗eɡᩴ\0\0᪈rrowĀlr᩼᪁eft;憺ight;憻ʀRSacd᪒᪔᪖᪚᪟»ཇ;擈st;抛irc;抚ash;抝nint;樐id;櫯cir;槂ubsĀ;u᪻᪼晣it»᪼ˬ᫇᫔᫺\0ᬊonĀ;eᫍᫎ䀺Ā;qÇÆɭ᫙\0\0᫢aĀ;t᫞᫟䀬;䁀ƀ;fl᫨᫩᫫戁îᅠeĀmx᫱᫶ent»᫩eóɍǧ᫾\0ᬇĀ;dኻᬂot;橭nôɆƀfryᬐᬔᬗ;쀀𝕔oäɔ脀©;sŕᬝr;愗Āaoᬥᬩrr;憵ss;朗Ācuᬲᬷr;쀀𝒸Ābpᬼ᭄Ā;eᭁᭂ櫏;櫑Ā;eᭉᭊ櫐;櫒dot;拯΀delprvw᭠᭬᭷ᮂᮬᯔ᯹arrĀlr᭨᭪;椸;椵ɰ᭲\0\0᭵r;拞c;拟arrĀ;p᭿ᮀ憶;椽̀;bcdosᮏᮐᮖᮡᮥᮨ截rcap;橈Āauᮛᮞp;橆p;橊ot;抍r;橅;쀀∪︀Ȁalrv᮵ᮿᯞᯣrrĀ;mᮼᮽ憷;椼yƀevwᯇᯔᯘqɰᯎ\0\0ᯒreã᭳uã᭵ee;拎edge;拏en耻¤䂤earrowĀlrᯮ᯳eft»ᮀight»ᮽeäᯝĀciᰁᰇoninôǷnt;戱lcty;挭ঀAHabcdefhijlorstuwz᰸᰻᰿ᱝᱩᱵᲊᲞᲬᲷ᳻᳿ᴍᵻᶑᶫᶻ᷆᷍rò΁ar;楥Ȁglrs᱈ᱍ᱒᱔ger;怠eth;愸òᄳhĀ;vᱚᱛ怐»ऊūᱡᱧarow;椏aã̕Āayᱮᱳron;䄏;䐴ƀ;ao̲ᱼᲄĀgrʿᲁr;懊tseq;橷ƀglmᲑᲔᲘ耻°䂰ta;䎴ptyv;榱ĀirᲣᲨsht;楿;쀀𝔡arĀlrᲳᲵ»ࣜ»သʀaegsv᳂͸᳖᳜᳠mƀ;oș᳊᳔ndĀ;ș᳑uit;晦amma;䏝in;拲ƀ;io᳧᳨᳸䃷de脀÷;o᳧ᳰntimes;拇nø᳷cy;䑒cɯᴆ\0\0ᴊrn;挞op;挍ʀlptuwᴘᴝᴢᵉᵕlar;䀤f;쀀𝕕ʀ;emps̋ᴭᴷᴽᵂqĀ;d͒ᴳot;扑inus;戸lus;戔quare;抡blebarwedgåúnƀadhᄮᵝᵧownarrowóᲃarpoonĀlrᵲᵶefôᲴighôᲶŢᵿᶅkaro÷གɯᶊ\0\0ᶎrn;挟op;挌ƀcotᶘᶣᶦĀryᶝᶡ;쀀𝒹;䑕l;槶rok;䄑Ādrᶰᶴot;拱iĀ;fᶺ᠖斿Āah᷀᷃ròЩaòྦangle;榦Āci᷒ᷕy;䑟grarr;柿ऀDacdefglmnopqrstuxḁḉḙḸոḼṉṡṾấắẽỡἪἷὄ὎὚ĀDoḆᴴoôᲉĀcsḎḔute耻é䃩ter;橮ȀaioyḢḧḱḶron;䄛rĀ;cḭḮ扖耻ê䃪lon;払;䑍ot;䄗ĀDrṁṅot;扒;쀀𝔢ƀ;rsṐṑṗ檚ave耻è䃨Ā;dṜṝ檖ot;檘Ȁ;ilsṪṫṲṴ檙nters;揧;愓Ā;dṹṺ檕ot;檗ƀapsẅẉẗcr;䄓tyƀ;svẒẓẕ戅et»ẓpĀ1;ẝẤijạả;怄;怅怃ĀgsẪẬ;䅋p;怂ĀgpẴẸon;䄙f;쀀𝕖ƀalsỄỎỒrĀ;sỊị拕l;槣us;橱iƀ;lvỚớở䎵on»ớ;䏵ȀcsuvỪỳἋἣĀioữḱrc»Ḯɩỹ\0\0ỻíՈantĀglἂἆtr»ṝess»Ṻƀaeiἒ἖Ἒls;䀽st;扟vĀ;DȵἠD;橸parsl;槥ĀDaἯἳot;打rr;楱ƀcdiἾὁỸr;愯oô͒ĀahὉὋ;䎷耻ð䃰Āmrὓὗl耻ë䃫o;悬ƀcipὡὤὧl;䀡sôծĀeoὬὴctatioîՙnentialåչৡᾒ\0ᾞ\0ᾡᾧ\0\0ῆῌ\0ΐ\0ῦῪ \0 ⁚llingdotseñṄy;䑄male;晀ƀilrᾭᾳ῁lig;耀ffiɩᾹ\0\0᾽g;耀ffig;耀ffl;쀀𝔣lig;耀filig;쀀fjƀaltῙ῜ῡt;晭ig;耀flns;斱of;䆒ǰ΅\0ῳf;쀀𝕗ĀakֿῷĀ;vῼ´拔;櫙artint;樍Āao‌⁕Ācs‑⁒ႉ‸⁅⁈\0⁐β•‥‧‪‬\0‮耻½䂽;慓耻¼䂼;慕;慙;慛Ƴ‴\0‶;慔;慖ʴ‾⁁\0\0⁃耻¾䂾;慗;慜5;慘ƶ⁌\0⁎;慚;慝8;慞l;恄wn;挢cr;쀀𝒻ࢀEabcdefgijlnorstv₂₉₟₥₰₴⃰⃵⃺⃿℃ℒℸ̗ℾ⅒↞Ā;lٍ₇;檌ƀcmpₐₕ₝ute;䇵maĀ;dₜ᳚䎳;檆reve;䄟Āiy₪₮rc;䄝;䐳ot;䄡Ȁ;lqsؾق₽⃉ƀ;qsؾٌ⃄lanô٥Ȁ;cdl٥⃒⃥⃕c;檩otĀ;o⃜⃝檀Ā;l⃢⃣檂;檄Ā;e⃪⃭쀀⋛︀s;檔r;쀀𝔤Ā;gٳ؛mel;愷cy;䑓Ȁ;Eajٚℌℎℐ;檒;檥;檤ȀEaesℛℝ℩ℴ;扩pĀ;p℣ℤ檊rox»ℤĀ;q℮ℯ檈Ā;q℮ℛim;拧pf;쀀𝕘Āci⅃ⅆr;愊mƀ;el٫ⅎ⅐;檎;檐茀>;cdlqr׮ⅠⅪⅮⅳⅹĀciⅥⅧ;檧r;橺ot;拗Par;榕uest;橼ʀadelsↄⅪ←ٖ↛ǰ↉\0↎proø₞r;楸qĀlqؿ↖lesó₈ií٫Āen↣↭rtneqq;쀀≩︀Å↪ԀAabcefkosy⇄⇇⇱⇵⇺∘∝∯≨≽ròΠȀilmr⇐⇔⇗⇛rsðᒄf»․ilôکĀdr⇠⇤cy;䑊ƀ;cwࣴ⇫⇯ir;楈;憭ar;意irc;䄥ƀalr∁∎∓rtsĀ;u∉∊晥it»∊lip;怦con;抹r;쀀𝔥sĀew∣∩arow;椥arow;椦ʀamopr∺∾≃≞≣rr;懿tht;戻kĀlr≉≓eftarrow;憩ightarrow;憪f;쀀𝕙bar;怕ƀclt≯≴≸r;쀀𝒽asè⇴rok;䄧Ābp⊂⊇ull;恃hen»ᱛૡ⊣\0⊪\0⊸⋅⋎\0⋕⋳\0\0⋸⌢⍧⍢⍿\0⎆⎪⎴cute耻í䃭ƀ;iyݱ⊰⊵rc耻î䃮;䐸Ācx⊼⊿y;䐵cl耻¡䂡ĀfrΟ⋉;쀀𝔦rave耻ì䃬Ȁ;inoܾ⋝⋩⋮Āin⋢⋦nt;樌t;戭fin;槜ta;愩lig;䄳ƀaop⋾⌚⌝ƀcgt⌅⌈⌗r;䄫ƀelpܟ⌏⌓inåގarôܠh;䄱f;抷ed;䆵ʀ;cfotӴ⌬⌱⌽⍁are;愅inĀ;t⌸⌹戞ie;槝doô⌙ʀ;celpݗ⍌⍐⍛⍡al;抺Āgr⍕⍙eróᕣã⍍arhk;樗rod;樼Ȁcgpt⍯⍲⍶⍻y;䑑on;䄯f;쀀𝕚a;䎹uest耻¿䂿Āci⎊⎏r;쀀𝒾nʀ;EdsvӴ⎛⎝⎡ӳ;拹ot;拵Ā;v⎦⎧拴;拳Ā;iݷ⎮lde;䄩ǫ⎸\0⎼cy;䑖l耻ï䃯̀cfmosu⏌⏗⏜⏡⏧⏵Āiy⏑⏕rc;䄵;䐹r;쀀𝔧ath;䈷pf;쀀𝕛ǣ⏬\0⏱r;쀀𝒿rcy;䑘kcy;䑔Ѐacfghjos␋␖␢␧␭␱␵␻ppaĀ;v␓␔䎺;䏰Āey␛␠dil;䄷;䐺r;쀀𝔨reen;䄸cy;䑅cy;䑜pf;쀀𝕜cr;쀀𝓀஀ABEHabcdefghjlmnoprstuv⑰⒁⒆⒍⒑┎┽╚▀♎♞♥♹♽⚚⚲⛘❝❨➋⟀⠁⠒ƀart⑷⑺⑼rò৆òΕail;椛arr;椎Ā;gঔ⒋;檋ar;楢ॣ⒥\0⒪\0⒱\0\0\0\0\0⒵Ⓔ\0ⓆⓈⓍ\0⓹ute;䄺mptyv;榴raîࡌbda;䎻gƀ;dlࢎⓁⓃ;榑åࢎ;檅uo耻«䂫rЀ;bfhlpst࢙ⓞⓦⓩ⓫⓮⓱⓵Ā;f࢝ⓣs;椟s;椝ë≒p;憫l;椹im;楳l;憢ƀ;ae⓿─┄檫il;椙Ā;s┉┊檭;쀀⪭︀ƀabr┕┙┝rr;椌rk;杲Āak┢┬cĀek┨┪;䁻;䁛Āes┱┳;榋lĀdu┹┻;榏;榍Ȁaeuy╆╋╖╘ron;䄾Ādi═╔il;䄼ìࢰâ┩;䐻Ȁcqrs╣╦╭╽a;椶uoĀ;rนᝆĀdu╲╷har;楧shar;楋h;憲ʀ;fgqs▋▌উ◳◿扤tʀahlrt▘▤▷◂◨rrowĀ;t࢙□aé⓶arpoonĀdu▯▴own»њp»०eftarrows;懇ightƀahs◍◖◞rrowĀ;sࣴࢧarpoonó྘quigarro÷⇰hreetimes;拋ƀ;qs▋ও◺lanôবʀ;cdgsব☊☍☝☨c;檨otĀ;o☔☕橿Ā;r☚☛檁;檃Ā;e☢☥쀀⋚︀s;檓ʀadegs☳☹☽♉♋pproøⓆot;拖qĀgq♃♅ôউgtò⒌ôছiíলƀilr♕࣡♚sht;楼;쀀𝔩Ā;Eজ♣;檑š♩♶rĀdu▲♮Ā;l॥♳;楪lk;斄cy;䑙ʀ;achtੈ⚈⚋⚑⚖rò◁orneòᴈard;楫ri;旺Āio⚟⚤dot;䅀ustĀ;a⚬⚭掰che»⚭ȀEaes⚻⚽⛉⛔;扨pĀ;p⛃⛄檉rox»⛄Ā;q⛎⛏檇Ā;q⛎⚻im;拦Ѐabnoptwz⛩⛴⛷✚✯❁❇❐Ānr⛮⛱g;柬r;懽rëࣁgƀlmr⛿✍✔eftĀar০✇ightá৲apsto;柼ightá৽parrowĀlr✥✩efô⓭ight;憬ƀafl✶✹✽r;榅;쀀𝕝us;樭imes;樴š❋❏st;戗áፎƀ;ef❗❘᠀旊nge»❘arĀ;l❤❥䀨t;榓ʀachmt❳❶❼➅➇ròࢨorneòᶌarĀ;d྘➃;業;怎ri;抿̀achiqt➘➝ੀ➢➮➻quo;怹r;쀀𝓁mƀ;egল➪➬;檍;檏Ābu┪➳oĀ;rฟ➹;怚rok;䅂萀<;cdhilqrࠫ⟒☹⟜⟠⟥⟪⟰Āci⟗⟙;檦r;橹reå◲mes;拉arr;楶uest;橻ĀPi⟵⟹ar;榖ƀ;ef⠀भ᠛旃rĀdu⠇⠍shar;楊har;楦Āen⠗⠡rtneqq;쀀≨︀Å⠞܀Dacdefhilnopsu⡀⡅⢂⢎⢓⢠⢥⢨⣚⣢⣤ઃ⣳⤂Dot;戺Ȁclpr⡎⡒⡣⡽r耻¯䂯Āet⡗⡙;時Ā;e⡞⡟朠se»⡟Ā;sျ⡨toȀ;dluျ⡳⡷⡻owîҌefôएðᏑker;斮Āoy⢇⢌mma;権;䐼ash;怔asuredangle»ᘦr;쀀𝔪o;愧ƀcdn⢯⢴⣉ro耻µ䂵Ȁ;acdᑤ⢽⣀⣄sôᚧir;櫰ot肻·Ƶusƀ;bd⣒ᤃ⣓戒Ā;uᴼ⣘;横ţ⣞⣡p;櫛ò−ðઁĀdp⣩⣮els;抧f;쀀𝕞Āct⣸⣽r;쀀𝓂pos»ᖝƀ;lm⤉⤊⤍䎼timap;抸ఀGLRVabcdefghijlmoprstuvw⥂⥓⥾⦉⦘⧚⧩⨕⨚⩘⩝⪃⪕⪤⪨⬄⬇⭄⭿⮮ⰴⱧⱼ⳩Āgt⥇⥋;쀀⋙̸Ā;v⥐௏쀀≫⃒ƀelt⥚⥲⥶ftĀar⥡⥧rrow;懍ightarrow;懎;쀀⋘̸Ā;v⥻ే쀀≪⃒ightarrow;懏ĀDd⦎⦓ash;抯ash;抮ʀbcnpt⦣⦧⦬⦱⧌la»˞ute;䅄g;쀀∠⃒ʀ;Eiop඄⦼⧀⧅⧈;쀀⩰̸d;쀀≋̸s;䅉roø඄urĀ;a⧓⧔普lĀ;s⧓ସdz⧟\0⧣p肻 ଷmpĀ;e௹ఀʀaeouy⧴⧾⨃⨐⨓ǰ⧹\0⧻;橃on;䅈dil;䅆ngĀ;dൾ⨊ot;쀀⩭̸p;橂;䐽ash;怓΀;Aadqsxஒ⨩⨭⨻⩁⩅⩐rr;懗rĀhr⨳⨶k;椤Ā;oᏲᏰot;쀀≐̸uiöୣĀei⩊⩎ar;椨í஘istĀ;s஠டr;쀀𝔫ȀEest௅⩦⩹⩼ƀ;qs஼⩭௡ƀ;qs஼௅⩴lanô௢ií௪Ā;rஶ⪁»ஷƀAap⪊⪍⪑rò⥱rr;憮ar;櫲ƀ;svྍ⪜ྌĀ;d⪡⪢拼;拺cy;䑚΀AEadest⪷⪺⪾⫂⫅⫶⫹rò⥦;쀀≦̸rr;憚r;急Ȁ;fqs఻⫎⫣⫯tĀar⫔⫙rro÷⫁ightarro÷⪐ƀ;qs఻⪺⫪lanôౕĀ;sౕ⫴»శiíౝĀ;rవ⫾iĀ;eచథiäඐĀpt⬌⬑f;쀀𝕟膀¬;in⬙⬚⬶䂬nȀ;Edvஉ⬤⬨⬮;쀀⋹̸ot;쀀⋵̸ǡஉ⬳⬵;拷;拶iĀ;vಸ⬼ǡಸ⭁⭃;拾;拽ƀaor⭋⭣⭩rȀ;ast୻⭕⭚⭟lleì୻l;쀀⫽⃥;쀀∂̸lint;樔ƀ;ceಒ⭰⭳uåಥĀ;cಘ⭸Ā;eಒ⭽ñಘȀAait⮈⮋⮝⮧rò⦈rrƀ;cw⮔⮕⮙憛;쀀⤳̸;쀀↝̸ghtarrow»⮕riĀ;eೋೖ΀chimpqu⮽⯍⯙⬄୸⯤⯯Ȁ;cerല⯆ഷ⯉uå൅;쀀𝓃ortɭ⬅\0\0⯖ará⭖mĀ;e൮⯟Ā;q൴൳suĀbp⯫⯭å೸åഋƀbcp⯶ⰑⰙȀ;Ees⯿ⰀഢⰄ抄;쀀⫅̸etĀ;eഛⰋqĀ;qണⰀcĀ;eലⰗñസȀ;EesⰢⰣൟⰧ抅;쀀⫆̸etĀ;e൘ⰮqĀ;qൠⰣȀgilrⰽⰿⱅⱇìௗlde耻ñ䃱çృiangleĀlrⱒⱜeftĀ;eచⱚñదightĀ;eೋⱥñ೗Ā;mⱬⱭ䎽ƀ;esⱴⱵⱹ䀣ro;愖p;怇ҀDHadgilrsⲏⲔⲙⲞⲣⲰⲶⳓⳣash;抭arr;椄p;쀀≍⃒ash;抬ĀetⲨⲬ;쀀≥⃒;쀀>⃒nfin;槞ƀAetⲽⳁⳅrr;椂;쀀≤⃒Ā;rⳊⳍ쀀<⃒ie;쀀⊴⃒ĀAtⳘⳜrr;椃rie;쀀⊵⃒im;쀀∼⃒ƀAan⳰⳴ⴂrr;懖rĀhr⳺⳽k;椣Ā;oᏧᏥear;椧ቓ᪕\0\0\0\0\0\0\0\0\0\0\0\0\0ⴭ\0ⴸⵈⵠⵥ⵲ⶄᬇ\0\0ⶍⶫ\0ⷈⷎ\0ⷜ⸙⸫⸾⹃Ācsⴱ᪗ute耻ó䃳ĀiyⴼⵅrĀ;c᪞ⵂ耻ô䃴;䐾ʀabios᪠ⵒⵗLjⵚlac;䅑v;樸old;榼lig;䅓Ācr⵩⵭ir;榿;쀀𝔬ͯ⵹\0\0⵼\0ⶂn;䋛ave耻ò䃲;槁Ābmⶈ෴ar;榵Ȁacitⶕ⶘ⶥⶨrò᪀Āir⶝ⶠr;榾oss;榻nå๒;槀ƀaeiⶱⶵⶹcr;䅍ga;䏉ƀcdnⷀⷅǍron;䎿;榶pf;쀀𝕠ƀaelⷔ⷗ǒr;榷rp;榹΀;adiosvⷪⷫⷮ⸈⸍⸐⸖戨rò᪆Ȁ;efmⷷⷸ⸂⸅橝rĀ;oⷾⷿ愴f»ⷿ耻ª䂪耻º䂺gof;抶r;橖lope;橗;橛ƀclo⸟⸡⸧ò⸁ash耻ø䃸l;折iŬⸯ⸴de耻õ䃵esĀ;aǛ⸺s;樶ml耻ö䃶bar;挽ૡ⹞\0⹽\0⺀⺝\0⺢⺹\0\0⻋ຜ\0⼓\0\0⼫⾼\0⿈rȀ;astЃ⹧⹲຅脀¶;l⹭⹮䂶leìЃɩ⹸\0\0⹻m;櫳;櫽y;䐿rʀcimpt⺋⺏⺓ᡥ⺗nt;䀥od;䀮il;怰enk;怱r;쀀𝔭ƀimo⺨⺰⺴Ā;v⺭⺮䏆;䏕maô੶ne;明ƀ;tv⺿⻀⻈䏀chfork»´;䏖Āau⻏⻟nĀck⻕⻝kĀ;h⇴⻛;愎ö⇴sҀ;abcdemst⻳⻴ᤈ⻹⻽⼄⼆⼊⼎䀫cir;樣ir;樢Āouᵀ⼂;樥;橲n肻±ຝim;樦wo;樧ƀipu⼙⼠⼥ntint;樕f;쀀𝕡nd耻£䂣Ԁ;Eaceinosu່⼿⽁⽄⽇⾁⾉⾒⽾⾶;檳p;檷uå໙Ā;c໎⽌̀;acens່⽙⽟⽦⽨⽾pproø⽃urlyeñ໙ñ໎ƀaes⽯⽶⽺pprox;檹qq;檵im;拨iíໟmeĀ;s⾈ຮ怲ƀEas⽸⾐⽺ð⽵ƀdfp໬⾙⾯ƀals⾠⾥⾪lar;挮ine;挒urf;挓Ā;t໻⾴ï໻rel;抰Āci⿀⿅r;쀀𝓅;䏈ncsp;怈̀fiopsu⿚⋢⿟⿥⿫⿱r;쀀𝔮pf;쀀𝕢rime;恗cr;쀀𝓆ƀaeo⿸〉〓tĀei⿾々rnionóڰnt;樖stĀ;e【】䀿ñἙô༔઀ABHabcdefhilmnoprstux぀けさすムㄎㄫㅇㅢㅲㆎ㈆㈕㈤㈩㉘㉮㉲㊐㊰㊷ƀartぇおがròႳòϝail;検aròᱥar;楤΀cdenqrtとふへみわゔヌĀeuねぱ;쀀∽̱te;䅕iãᅮmptyv;榳gȀ;del࿑らるろ;榒;榥å࿑uo耻»䂻rր;abcfhlpstw࿜ガクシスゼゾダッデナp;極Ā;f࿠ゴs;椠;椳s;椞ë≝ð✮l;楅im;楴l;憣;憝Āaiパフil;椚oĀ;nホボ戶aló༞ƀabrョリヮrò៥rk;杳ĀakンヽcĀekヹ・;䁽;䁝Āes㄂㄄;榌lĀduㄊㄌ;榎;榐Ȁaeuyㄗㄜㄧㄩron;䅙Ādiㄡㄥil;䅗ì࿲âヺ;䑀Ȁclqsㄴㄷㄽㅄa;椷dhar;楩uoĀ;rȎȍh;憳ƀacgㅎㅟངlȀ;ipsླྀㅘㅛႜnåႻarôྩt;断ƀilrㅩဣㅮsht;楽;쀀𝔯ĀaoㅷㆆrĀduㅽㅿ»ѻĀ;l႑ㆄ;楬Ā;vㆋㆌ䏁;䏱ƀgns㆕ㇹㇼht̀ahlrstㆤㆰ㇂㇘㇤㇮rrowĀ;t࿜ㆭaéトarpoonĀduㆻㆿowîㅾp»႒eftĀah㇊㇐rrowó࿪arpoonóՑightarrows;應quigarro÷ニhreetimes;拌g;䋚ingdotseñἲƀahm㈍㈐㈓rò࿪aòՑ;怏oustĀ;a㈞㈟掱che»㈟mid;櫮Ȁabpt㈲㈽㉀㉒Ānr㈷㈺g;柭r;懾rëဃƀafl㉇㉊㉎r;榆;쀀𝕣us;樮imes;樵Āap㉝㉧rĀ;g㉣㉤䀩t;榔olint;樒arò㇣Ȁachq㉻㊀Ⴜ㊅quo;怺r;쀀𝓇Ābu・㊊oĀ;rȔȓƀhir㊗㊛㊠reåㇸmes;拊iȀ;efl㊪ၙᠡ㊫方tri;槎luhar;楨;愞ൡ㋕㋛㋟㌬㌸㍱\0㍺㎤\0\0㏬㏰\0㐨㑈㑚㒭㒱㓊㓱\0㘖\0\0㘳cute;䅛quï➺Ԁ;Eaceinpsyᇭ㋳㋵㋿㌂㌋㌏㌟㌦㌩;檴ǰ㋺\0㋼;檸on;䅡uåᇾĀ;dᇳ㌇il;䅟rc;䅝ƀEas㌖㌘㌛;檶p;檺im;择olint;樓iíሄ;䑁otƀ;be㌴ᵇ㌵担;橦΀Aacmstx㍆㍊㍗㍛㍞㍣㍭rr;懘rĀhr㍐㍒ë∨Ā;oਸ਼਴t耻§䂧i;䀻war;椩mĀin㍩ðnuóñt;朶rĀ;o㍶⁕쀀𝔰Ȁacoy㎂㎆㎑㎠rp;景Āhy㎋㎏cy;䑉;䑈rtɭ㎙\0\0㎜iäᑤaraì⹯耻­䂭Āgm㎨㎴maƀ;fv㎱㎲㎲䏃;䏂Ѐ;deglnprካ㏅㏉㏎㏖㏞㏡㏦ot;橪Ā;q኱ኰĀ;E㏓㏔檞;檠Ā;E㏛㏜檝;檟e;扆lus;樤arr;楲aròᄽȀaeit㏸㐈㐏㐗Āls㏽㐄lsetmé㍪hp;樳parsl;槤Ādlᑣ㐔e;挣Ā;e㐜㐝檪Ā;s㐢㐣檬;쀀⪬︀ƀflp㐮㐳㑂tcy;䑌Ā;b㐸㐹䀯Ā;a㐾㐿槄r;挿f;쀀𝕤aĀdr㑍ЂesĀ;u㑔㑕晠it»㑕ƀcsu㑠㑹㒟Āau㑥㑯pĀ;sᆈ㑫;쀀⊓︀pĀ;sᆴ㑵;쀀⊔︀uĀbp㑿㒏ƀ;esᆗᆜ㒆etĀ;eᆗ㒍ñᆝƀ;esᆨᆭ㒖etĀ;eᆨ㒝ñᆮƀ;afᅻ㒦ְrť㒫ֱ»ᅼaròᅈȀcemt㒹㒾㓂㓅r;쀀𝓈tmîñiì㐕aræᆾĀar㓎㓕rĀ;f㓔ឿ昆Āan㓚㓭ightĀep㓣㓪psiloîỠhé⺯s»⡒ʀbcmnp㓻㕞ሉ㖋㖎Ҁ;Edemnprs㔎㔏㔑㔕㔞㔣㔬㔱㔶抂;櫅ot;檽Ā;dᇚ㔚ot;櫃ult;櫁ĀEe㔨㔪;櫋;把lus;檿arr;楹ƀeiu㔽㕒㕕tƀ;en㔎㕅㕋qĀ;qᇚ㔏eqĀ;q㔫㔨m;櫇Ābp㕚㕜;櫕;櫓c̀;acensᇭ㕬㕲㕹㕻㌦pproø㋺urlyeñᇾñᇳƀaes㖂㖈㌛pproø㌚qñ㌗g;晪ڀ123;Edehlmnps㖩㖬㖯ሜ㖲㖴㗀㗉㗕㗚㗟㗨㗭耻¹䂹耻²䂲耻³䂳;櫆Āos㖹㖼t;檾ub;櫘Ā;dሢ㗅ot;櫄sĀou㗏㗒l;柉b;櫗arr;楻ult;櫂ĀEe㗤㗦;櫌;抋lus;櫀ƀeiu㗴㘉㘌tƀ;enሜ㗼㘂qĀ;qሢ㖲eqĀ;q㗧㗤m;櫈Ābp㘑㘓;櫔;櫖ƀAan㘜㘠㘭rr;懙rĀhr㘦㘨ë∮Ā;oਫ਩war;椪lig耻ß䃟௡㙑㙝㙠ዎ㙳㙹\0㙾㛂\0\0\0\0\0㛛㜃\0㜉㝬\0\0\0㞇ɲ㙖\0\0㙛get;挖;䏄rë๟ƀaey㙦㙫㙰ron;䅥dil;䅣;䑂lrec;挕r;쀀𝔱Ȁeiko㚆㚝㚵㚼Dz㚋\0㚑eĀ4fኄኁaƀ;sv㚘㚙㚛䎸ym;䏑Ācn㚢㚲kĀas㚨㚮pproø዁im»ኬsðኞĀas㚺㚮ð዁rn耻þ䃾Ǭ̟㛆⋧es膀×;bd㛏㛐㛘䃗Ā;aᤏ㛕r;樱;樰ƀeps㛡㛣㜀á⩍Ȁ;bcf҆㛬㛰㛴ot;挶ir;櫱Ā;o㛹㛼쀀𝕥rk;櫚á㍢rime;怴ƀaip㜏㜒㝤dåቈ΀adempst㜡㝍㝀㝑㝗㝜㝟ngleʀ;dlqr㜰㜱㜶㝀㝂斵own»ᶻeftĀ;e⠀㜾ñम;扜ightĀ;e㊪㝋ñၚot;旬inus;樺lus;樹b;槍ime;樻ezium;揢ƀcht㝲㝽㞁Āry㝷㝻;쀀𝓉;䑆cy;䑛rok;䅧Āio㞋㞎xô᝷headĀlr㞗㞠eftarro÷ࡏightarrow»ཝऀAHabcdfghlmoprstuw㟐㟓㟗㟤㟰㟼㠎㠜㠣㠴㡑㡝㡫㢩㣌㣒㣪㣶ròϭar;楣Ācr㟜㟢ute耻ú䃺òᅐrǣ㟪\0㟭y;䑞ve;䅭Āiy㟵㟺rc耻û䃻;䑃ƀabh㠃㠆㠋ròᎭlac;䅱aòᏃĀir㠓㠘sht;楾;쀀𝔲rave耻ù䃹š㠧㠱rĀlr㠬㠮»ॗ»ႃlk;斀Āct㠹㡍ɯ㠿\0\0㡊rnĀ;e㡅㡆挜r»㡆op;挏ri;旸Āal㡖㡚cr;䅫肻¨͉Āgp㡢㡦on;䅳f;쀀𝕦̀adhlsuᅋ㡸㡽፲㢑㢠ownáᎳarpoonĀlr㢈㢌efô㠭ighô㠯iƀ;hl㢙㢚㢜䏅»ᏺon»㢚parrows;懈ƀcit㢰㣄㣈ɯ㢶\0\0㣁rnĀ;e㢼㢽挝r»㢽op;挎ng;䅯ri;旹cr;쀀𝓊ƀdir㣙㣝㣢ot;拰lde;䅩iĀ;f㜰㣨»᠓Āam㣯㣲rò㢨l耻ü䃼angle;榧ހABDacdeflnoprsz㤜㤟㤩㤭㦵㦸㦽㧟㧤㧨㧳㧹㧽㨁㨠ròϷarĀ;v㤦㤧櫨;櫩asèϡĀnr㤲㤷grt;榜΀eknprst㓣㥆㥋㥒㥝㥤㦖appá␕othinçẖƀhir㓫⻈㥙opô⾵Ā;hᎷ㥢ïㆍĀiu㥩㥭gmá㎳Ābp㥲㦄setneqĀ;q㥽㦀쀀⊊︀;쀀⫋︀setneqĀ;q㦏㦒쀀⊋︀;쀀⫌︀Āhr㦛㦟etá㚜iangleĀlr㦪㦯eft»थight»ၑy;䐲ash»ံƀelr㧄㧒㧗ƀ;beⷪ㧋㧏ar;抻q;扚lip;拮Ābt㧜ᑨaòᑩr;쀀𝔳tré㦮suĀbp㧯㧱»ജ»൙pf;쀀𝕧roð໻tré㦴Ācu㨆㨋r;쀀𝓋Ābp㨐㨘nĀEe㦀㨖»㥾nĀEe㦒㨞»㦐igzag;榚΀cefoprs㨶㨻㩖㩛㩔㩡㩪irc;䅵Ādi㩀㩑Ābg㩅㩉ar;機eĀ;qᗺ㩏;扙erp;愘r;쀀𝔴pf;쀀𝕨Ā;eᑹ㩦atèᑹcr;쀀𝓌ૣណ㪇\0㪋\0㪐㪛\0\0㪝㪨㪫㪯\0\0㫃㫎\0㫘ៜ៟tré៑r;쀀𝔵ĀAa㪔㪗ròσrò৶;䎾ĀAa㪡㪤ròθrò৫að✓is;拻ƀdptឤ㪵㪾Āfl㪺ឩ;쀀𝕩imåឲĀAa㫇㫊ròώròਁĀcq㫒ីr;쀀𝓍Āpt៖㫜ré។Ѐacefiosu㫰㫽㬈㬌㬑㬕㬛㬡cĀuy㫶㫻te耻ý䃽;䑏Āiy㬂㬆rc;䅷;䑋n耻¥䂥r;쀀𝔶cy;䑗pf;쀀𝕪cr;쀀𝓎Ācm㬦㬩y;䑎l耻ÿ䃿Ԁacdefhiosw㭂㭈㭔㭘㭤㭩㭭㭴㭺㮀cute;䅺Āay㭍㭒ron;䅾;䐷ot;䅼Āet㭝㭡træᕟa;䎶r;쀀𝔷cy;䐶grarr;懝pf;쀀𝕫cr;쀀𝓏Ājn㮅㮇;怍j;怌'.split("").map(t=>t.charCodeAt(0))),f$=new Map([[0,65533],[128,8364],[130,8218],[131,402],[132,8222],[133,8230],[134,8224],[135,8225],[136,710],[137,8240],[138,352],[139,8249],[140,338],[142,381],[145,8216],[146,8217],[147,8220],[148,8221],[149,8226],[150,8211],[151,8212],[152,732],[153,8482],[154,353],[155,8250],[156,339],[158,382],[159,376]]);function h$(t){var e;return t>=55296&&t<=57343||t>1114111?65533:(e=f$.get(t))!==null&&e!==void 0?e:t}var Wn;(function(t){t[t.NUM=35]="NUM",t[t.SEMI=59]="SEMI",t[t.EQUALS=61]="EQUALS",t[t.ZERO=48]="ZERO",t[t.NINE=57]="NINE",t[t.LOWER_A=97]="LOWER_A",t[t.LOWER_F=102]="LOWER_F",t[t.LOWER_X=120]="LOWER_X",t[t.LOWER_Z=122]="LOWER_Z",t[t.UPPER_A=65]="UPPER_A",t[t.UPPER_F=70]="UPPER_F",t[t.UPPER_Z=90]="UPPER_Z"})(Wn||(Wn={}));const p$=32;var ro;(function(t){t[t.VALUE_LENGTH=49152]="VALUE_LENGTH",t[t.BRANCH_LENGTH=16256]="BRANCH_LENGTH",t[t.JUMP_TABLE=127]="JUMP_TABLE"})(ro||(ro={}));function ob(t){return t>=Wn.ZERO&&t<=Wn.NINE}function m$(t){return t>=Wn.UPPER_A&&t<=Wn.UPPER_F||t>=Wn.LOWER_A&&t<=Wn.LOWER_F}function g$(t){return t>=Wn.UPPER_A&&t<=Wn.UPPER_Z||t>=Wn.LOWER_A&&t<=Wn.LOWER_Z||ob(t)}function b$(t){return t===Wn.EQUALS||g$(t)}var jn;(function(t){t[t.EntityStart=0]="EntityStart",t[t.NumericStart=1]="NumericStart",t[t.NumericDecimal=2]="NumericDecimal",t[t.NumericHex=3]="NumericHex",t[t.NamedEntity=4]="NamedEntity"})(jn||(jn={}));var hs;(function(t){t[t.Legacy=0]="Legacy",t[t.Strict=1]="Strict",t[t.Attribute=2]="Attribute"})(hs||(hs={}));class E${constructor(e,n,r){this.decodeTree=e,this.emitCodePoint=n,this.errors=r,this.state=jn.EntityStart,this.consumed=1,this.result=0,this.treeIndex=0,this.excess=1,this.decodeMode=hs.Strict}startEntity(e){this.decodeMode=e,this.state=jn.EntityStart,this.result=0,this.treeIndex=0,this.excess=1,this.consumed=1}write(e,n){switch(this.state){case jn.EntityStart:return e.charCodeAt(n)===Wn.NUM?(this.state=jn.NumericStart,this.consumed+=1,this.stateNumericStart(e,n+1)):(this.state=jn.NamedEntity,this.stateNamedEntity(e,n));case jn.NumericStart:return this.stateNumericStart(e,n);case jn.NumericDecimal:return this.stateNumericDecimal(e,n);case jn.NumericHex:return this.stateNumericHex(e,n);case jn.NamedEntity:return this.stateNamedEntity(e,n)}}stateNumericStart(e,n){return n>=e.length?-1:(e.charCodeAt(n)|p$)===Wn.LOWER_X?(this.state=jn.NumericHex,this.consumed+=1,this.stateNumericHex(e,n+1)):(this.state=jn.NumericDecimal,this.stateNumericDecimal(e,n))}addToNumericResult(e,n,r,i){if(n!==r){const s=r-n;this.result=this.result*Math.pow(i,s)+Number.parseInt(e.substr(n,s),i),this.consumed+=s}}stateNumericHex(e,n){const r=n;for(;n>14;for(;n>14,s!==0){if(o===Wn.SEMI)return this.emitNamedEntityData(this.treeIndex,s,this.consumed+this.excess);this.decodeMode!==hs.Strict&&(this.result=this.treeIndex,this.consumed+=this.excess,this.excess=0)}}return-1}emitNotTerminatedNamedEntity(){var e;const{result:n,decodeTree:r}=this,i=(r[n]&ro.VALUE_LENGTH)>>14;return this.emitNamedEntityData(n,i,this.consumed),(e=this.errors)===null||e===void 0||e.missingSemicolonAfterCharacterReference(),this.consumed}emitNamedEntityData(e,n,r){const{decodeTree:i}=this;return this.emitCodePoint(n===1?i[e]&~ro.VALUE_LENGTH:i[e+1],r),n===3&&this.emitCodePoint(i[e+2],r),r}end(){var e;switch(this.state){case jn.NamedEntity:return this.result!==0&&(this.decodeMode!==hs.Attribute||this.result===this.treeIndex)?this.emitNotTerminatedNamedEntity():0;case jn.NumericDecimal:return this.emitNumericEntity(0,2);case jn.NumericHex:return this.emitNumericEntity(0,3);case jn.NumericStart:return(e=this.errors)===null||e===void 0||e.absenceOfDigitsInNumericCharacterReference(this.consumed),0;case jn.EntityStart:return 0}}}function y$(t,e,n,r){const i=(e&ro.BRANCH_LENGTH)>>7,s=e&ro.JUMP_TABLE;if(i===0)return s!==0&&r===s?n:-1;if(s){const c=r-s;return c<0||c>=i?-1:t[n+c]-1}let o=n,l=o+i-1;for(;o<=l;){const c=o+l>>>1,d=t[c];if(dr)l=c-1;else return t[c+i]}return-1}var be;(function(t){t.HTML="http://www.w3.org/1999/xhtml",t.MATHML="http://www.w3.org/1998/Math/MathML",t.SVG="http://www.w3.org/2000/svg",t.XLINK="http://www.w3.org/1999/xlink",t.XML="http://www.w3.org/XML/1998/namespace",t.XMLNS="http://www.w3.org/2000/xmlns/"})(be||(be={}));var Wo;(function(t){t.TYPE="type",t.ACTION="action",t.ENCODING="encoding",t.PROMPT="prompt",t.NAME="name",t.COLOR="color",t.FACE="face",t.SIZE="size"})(Wo||(Wo={}));var ni;(function(t){t.NO_QUIRKS="no-quirks",t.QUIRKS="quirks",t.LIMITED_QUIRKS="limited-quirks"})(ni||(ni={}));var re;(function(t){t.A="a",t.ADDRESS="address",t.ANNOTATION_XML="annotation-xml",t.APPLET="applet",t.AREA="area",t.ARTICLE="article",t.ASIDE="aside",t.B="b",t.BASE="base",t.BASEFONT="basefont",t.BGSOUND="bgsound",t.BIG="big",t.BLOCKQUOTE="blockquote",t.BODY="body",t.BR="br",t.BUTTON="button",t.CAPTION="caption",t.CENTER="center",t.CODE="code",t.COL="col",t.COLGROUP="colgroup",t.DD="dd",t.DESC="desc",t.DETAILS="details",t.DIALOG="dialog",t.DIR="dir",t.DIV="div",t.DL="dl",t.DT="dt",t.EM="em",t.EMBED="embed",t.FIELDSET="fieldset",t.FIGCAPTION="figcaption",t.FIGURE="figure",t.FONT="font",t.FOOTER="footer",t.FOREIGN_OBJECT="foreignObject",t.FORM="form",t.FRAME="frame",t.FRAMESET="frameset",t.H1="h1",t.H2="h2",t.H3="h3",t.H4="h4",t.H5="h5",t.H6="h6",t.HEAD="head",t.HEADER="header",t.HGROUP="hgroup",t.HR="hr",t.HTML="html",t.I="i",t.IMG="img",t.IMAGE="image",t.INPUT="input",t.IFRAME="iframe",t.KEYGEN="keygen",t.LABEL="label",t.LI="li",t.LINK="link",t.LISTING="listing",t.MAIN="main",t.MALIGNMARK="malignmark",t.MARQUEE="marquee",t.MATH="math",t.MENU="menu",t.META="meta",t.MGLYPH="mglyph",t.MI="mi",t.MO="mo",t.MN="mn",t.MS="ms",t.MTEXT="mtext",t.NAV="nav",t.NOBR="nobr",t.NOFRAMES="noframes",t.NOEMBED="noembed",t.NOSCRIPT="noscript",t.OBJECT="object",t.OL="ol",t.OPTGROUP="optgroup",t.OPTION="option",t.P="p",t.PARAM="param",t.PLAINTEXT="plaintext",t.PRE="pre",t.RB="rb",t.RP="rp",t.RT="rt",t.RTC="rtc",t.RUBY="ruby",t.S="s",t.SCRIPT="script",t.SEARCH="search",t.SECTION="section",t.SELECT="select",t.SOURCE="source",t.SMALL="small",t.SPAN="span",t.STRIKE="strike",t.STRONG="strong",t.STYLE="style",t.SUB="sub",t.SUMMARY="summary",t.SUP="sup",t.TABLE="table",t.TBODY="tbody",t.TEMPLATE="template",t.TEXTAREA="textarea",t.TFOOT="tfoot",t.TD="td",t.TH="th",t.THEAD="thead",t.TITLE="title",t.TR="tr",t.TRACK="track",t.TT="tt",t.U="u",t.UL="ul",t.SVG="svg",t.VAR="var",t.WBR="wbr",t.XMP="xmp"})(re||(re={}));var E;(function(t){t[t.UNKNOWN=0]="UNKNOWN",t[t.A=1]="A",t[t.ADDRESS=2]="ADDRESS",t[t.ANNOTATION_XML=3]="ANNOTATION_XML",t[t.APPLET=4]="APPLET",t[t.AREA=5]="AREA",t[t.ARTICLE=6]="ARTICLE",t[t.ASIDE=7]="ASIDE",t[t.B=8]="B",t[t.BASE=9]="BASE",t[t.BASEFONT=10]="BASEFONT",t[t.BGSOUND=11]="BGSOUND",t[t.BIG=12]="BIG",t[t.BLOCKQUOTE=13]="BLOCKQUOTE",t[t.BODY=14]="BODY",t[t.BR=15]="BR",t[t.BUTTON=16]="BUTTON",t[t.CAPTION=17]="CAPTION",t[t.CENTER=18]="CENTER",t[t.CODE=19]="CODE",t[t.COL=20]="COL",t[t.COLGROUP=21]="COLGROUP",t[t.DD=22]="DD",t[t.DESC=23]="DESC",t[t.DETAILS=24]="DETAILS",t[t.DIALOG=25]="DIALOG",t[t.DIR=26]="DIR",t[t.DIV=27]="DIV",t[t.DL=28]="DL",t[t.DT=29]="DT",t[t.EM=30]="EM",t[t.EMBED=31]="EMBED",t[t.FIELDSET=32]="FIELDSET",t[t.FIGCAPTION=33]="FIGCAPTION",t[t.FIGURE=34]="FIGURE",t[t.FONT=35]="FONT",t[t.FOOTER=36]="FOOTER",t[t.FOREIGN_OBJECT=37]="FOREIGN_OBJECT",t[t.FORM=38]="FORM",t[t.FRAME=39]="FRAME",t[t.FRAMESET=40]="FRAMESET",t[t.H1=41]="H1",t[t.H2=42]="H2",t[t.H3=43]="H3",t[t.H4=44]="H4",t[t.H5=45]="H5",t[t.H6=46]="H6",t[t.HEAD=47]="HEAD",t[t.HEADER=48]="HEADER",t[t.HGROUP=49]="HGROUP",t[t.HR=50]="HR",t[t.HTML=51]="HTML",t[t.I=52]="I",t[t.IMG=53]="IMG",t[t.IMAGE=54]="IMAGE",t[t.INPUT=55]="INPUT",t[t.IFRAME=56]="IFRAME",t[t.KEYGEN=57]="KEYGEN",t[t.LABEL=58]="LABEL",t[t.LI=59]="LI",t[t.LINK=60]="LINK",t[t.LISTING=61]="LISTING",t[t.MAIN=62]="MAIN",t[t.MALIGNMARK=63]="MALIGNMARK",t[t.MARQUEE=64]="MARQUEE",t[t.MATH=65]="MATH",t[t.MENU=66]="MENU",t[t.META=67]="META",t[t.MGLYPH=68]="MGLYPH",t[t.MI=69]="MI",t[t.MO=70]="MO",t[t.MN=71]="MN",t[t.MS=72]="MS",t[t.MTEXT=73]="MTEXT",t[t.NAV=74]="NAV",t[t.NOBR=75]="NOBR",t[t.NOFRAMES=76]="NOFRAMES",t[t.NOEMBED=77]="NOEMBED",t[t.NOSCRIPT=78]="NOSCRIPT",t[t.OBJECT=79]="OBJECT",t[t.OL=80]="OL",t[t.OPTGROUP=81]="OPTGROUP",t[t.OPTION=82]="OPTION",t[t.P=83]="P",t[t.PARAM=84]="PARAM",t[t.PLAINTEXT=85]="PLAINTEXT",t[t.PRE=86]="PRE",t[t.RB=87]="RB",t[t.RP=88]="RP",t[t.RT=89]="RT",t[t.RTC=90]="RTC",t[t.RUBY=91]="RUBY",t[t.S=92]="S",t[t.SCRIPT=93]="SCRIPT",t[t.SEARCH=94]="SEARCH",t[t.SECTION=95]="SECTION",t[t.SELECT=96]="SELECT",t[t.SOURCE=97]="SOURCE",t[t.SMALL=98]="SMALL",t[t.SPAN=99]="SPAN",t[t.STRIKE=100]="STRIKE",t[t.STRONG=101]="STRONG",t[t.STYLE=102]="STYLE",t[t.SUB=103]="SUB",t[t.SUMMARY=104]="SUMMARY",t[t.SUP=105]="SUP",t[t.TABLE=106]="TABLE",t[t.TBODY=107]="TBODY",t[t.TEMPLATE=108]="TEMPLATE",t[t.TEXTAREA=109]="TEXTAREA",t[t.TFOOT=110]="TFOOT",t[t.TD=111]="TD",t[t.TH=112]="TH",t[t.THEAD=113]="THEAD",t[t.TITLE=114]="TITLE",t[t.TR=115]="TR",t[t.TRACK=116]="TRACK",t[t.TT=117]="TT",t[t.U=118]="U",t[t.UL=119]="UL",t[t.SVG=120]="SVG",t[t.VAR=121]="VAR",t[t.WBR=122]="WBR",t[t.XMP=123]="XMP"})(E||(E={}));const x$=new Map([[re.A,E.A],[re.ADDRESS,E.ADDRESS],[re.ANNOTATION_XML,E.ANNOTATION_XML],[re.APPLET,E.APPLET],[re.AREA,E.AREA],[re.ARTICLE,E.ARTICLE],[re.ASIDE,E.ASIDE],[re.B,E.B],[re.BASE,E.BASE],[re.BASEFONT,E.BASEFONT],[re.BGSOUND,E.BGSOUND],[re.BIG,E.BIG],[re.BLOCKQUOTE,E.BLOCKQUOTE],[re.BODY,E.BODY],[re.BR,E.BR],[re.BUTTON,E.BUTTON],[re.CAPTION,E.CAPTION],[re.CENTER,E.CENTER],[re.CODE,E.CODE],[re.COL,E.COL],[re.COLGROUP,E.COLGROUP],[re.DD,E.DD],[re.DESC,E.DESC],[re.DETAILS,E.DETAILS],[re.DIALOG,E.DIALOG],[re.DIR,E.DIR],[re.DIV,E.DIV],[re.DL,E.DL],[re.DT,E.DT],[re.EM,E.EM],[re.EMBED,E.EMBED],[re.FIELDSET,E.FIELDSET],[re.FIGCAPTION,E.FIGCAPTION],[re.FIGURE,E.FIGURE],[re.FONT,E.FONT],[re.FOOTER,E.FOOTER],[re.FOREIGN_OBJECT,E.FOREIGN_OBJECT],[re.FORM,E.FORM],[re.FRAME,E.FRAME],[re.FRAMESET,E.FRAMESET],[re.H1,E.H1],[re.H2,E.H2],[re.H3,E.H3],[re.H4,E.H4],[re.H5,E.H5],[re.H6,E.H6],[re.HEAD,E.HEAD],[re.HEADER,E.HEADER],[re.HGROUP,E.HGROUP],[re.HR,E.HR],[re.HTML,E.HTML],[re.I,E.I],[re.IMG,E.IMG],[re.IMAGE,E.IMAGE],[re.INPUT,E.INPUT],[re.IFRAME,E.IFRAME],[re.KEYGEN,E.KEYGEN],[re.LABEL,E.LABEL],[re.LI,E.LI],[re.LINK,E.LINK],[re.LISTING,E.LISTING],[re.MAIN,E.MAIN],[re.MALIGNMARK,E.MALIGNMARK],[re.MARQUEE,E.MARQUEE],[re.MATH,E.MATH],[re.MENU,E.MENU],[re.META,E.META],[re.MGLYPH,E.MGLYPH],[re.MI,E.MI],[re.MO,E.MO],[re.MN,E.MN],[re.MS,E.MS],[re.MTEXT,E.MTEXT],[re.NAV,E.NAV],[re.NOBR,E.NOBR],[re.NOFRAMES,E.NOFRAMES],[re.NOEMBED,E.NOEMBED],[re.NOSCRIPT,E.NOSCRIPT],[re.OBJECT,E.OBJECT],[re.OL,E.OL],[re.OPTGROUP,E.OPTGROUP],[re.OPTION,E.OPTION],[re.P,E.P],[re.PARAM,E.PARAM],[re.PLAINTEXT,E.PLAINTEXT],[re.PRE,E.PRE],[re.RB,E.RB],[re.RP,E.RP],[re.RT,E.RT],[re.RTC,E.RTC],[re.RUBY,E.RUBY],[re.S,E.S],[re.SCRIPT,E.SCRIPT],[re.SEARCH,E.SEARCH],[re.SECTION,E.SECTION],[re.SELECT,E.SELECT],[re.SOURCE,E.SOURCE],[re.SMALL,E.SMALL],[re.SPAN,E.SPAN],[re.STRIKE,E.STRIKE],[re.STRONG,E.STRONG],[re.STYLE,E.STYLE],[re.SUB,E.SUB],[re.SUMMARY,E.SUMMARY],[re.SUP,E.SUP],[re.TABLE,E.TABLE],[re.TBODY,E.TBODY],[re.TEMPLATE,E.TEMPLATE],[re.TEXTAREA,E.TEXTAREA],[re.TFOOT,E.TFOOT],[re.TD,E.TD],[re.TH,E.TH],[re.THEAD,E.THEAD],[re.TITLE,E.TITLE],[re.TR,E.TR],[re.TRACK,E.TRACK],[re.TT,E.TT],[re.U,E.U],[re.UL,E.UL],[re.SVG,E.SVG],[re.VAR,E.VAR],[re.WBR,E.WBR],[re.XMP,E.XMP]]);function Fl(t){var e;return(e=x$.get(t))!==null&&e!==void 0?e:E.UNKNOWN}const ve=E,v$={[be.HTML]:new Set([ve.ADDRESS,ve.APPLET,ve.AREA,ve.ARTICLE,ve.ASIDE,ve.BASE,ve.BASEFONT,ve.BGSOUND,ve.BLOCKQUOTE,ve.BODY,ve.BR,ve.BUTTON,ve.CAPTION,ve.CENTER,ve.COL,ve.COLGROUP,ve.DD,ve.DETAILS,ve.DIR,ve.DIV,ve.DL,ve.DT,ve.EMBED,ve.FIELDSET,ve.FIGCAPTION,ve.FIGURE,ve.FOOTER,ve.FORM,ve.FRAME,ve.FRAMESET,ve.H1,ve.H2,ve.H3,ve.H4,ve.H5,ve.H6,ve.HEAD,ve.HEADER,ve.HGROUP,ve.HR,ve.HTML,ve.IFRAME,ve.IMG,ve.INPUT,ve.LI,ve.LINK,ve.LISTING,ve.MAIN,ve.MARQUEE,ve.MENU,ve.META,ve.NAV,ve.NOEMBED,ve.NOFRAMES,ve.NOSCRIPT,ve.OBJECT,ve.OL,ve.P,ve.PARAM,ve.PLAINTEXT,ve.PRE,ve.SCRIPT,ve.SECTION,ve.SELECT,ve.SOURCE,ve.STYLE,ve.SUMMARY,ve.TABLE,ve.TBODY,ve.TD,ve.TEMPLATE,ve.TEXTAREA,ve.TFOOT,ve.TH,ve.THEAD,ve.TITLE,ve.TR,ve.TRACK,ve.UL,ve.WBR,ve.XMP]),[be.MATHML]:new Set([ve.MI,ve.MO,ve.MN,ve.MS,ve.MTEXT,ve.ANNOTATION_XML]),[be.SVG]:new Set([ve.TITLE,ve.FOREIGN_OBJECT,ve.DESC]),[be.XLINK]:new Set,[be.XML]:new Set,[be.XMLNS]:new Set},ab=new Set([ve.H1,ve.H2,ve.H3,ve.H4,ve.H5,ve.H6]);re.STYLE,re.SCRIPT,re.XMP,re.IFRAME,re.NOEMBED,re.NOFRAMES,re.PLAINTEXT;var B;(function(t){t[t.DATA=0]="DATA",t[t.RCDATA=1]="RCDATA",t[t.RAWTEXT=2]="RAWTEXT",t[t.SCRIPT_DATA=3]="SCRIPT_DATA",t[t.PLAINTEXT=4]="PLAINTEXT",t[t.TAG_OPEN=5]="TAG_OPEN",t[t.END_TAG_OPEN=6]="END_TAG_OPEN",t[t.TAG_NAME=7]="TAG_NAME",t[t.RCDATA_LESS_THAN_SIGN=8]="RCDATA_LESS_THAN_SIGN",t[t.RCDATA_END_TAG_OPEN=9]="RCDATA_END_TAG_OPEN",t[t.RCDATA_END_TAG_NAME=10]="RCDATA_END_TAG_NAME",t[t.RAWTEXT_LESS_THAN_SIGN=11]="RAWTEXT_LESS_THAN_SIGN",t[t.RAWTEXT_END_TAG_OPEN=12]="RAWTEXT_END_TAG_OPEN",t[t.RAWTEXT_END_TAG_NAME=13]="RAWTEXT_END_TAG_NAME",t[t.SCRIPT_DATA_LESS_THAN_SIGN=14]="SCRIPT_DATA_LESS_THAN_SIGN",t[t.SCRIPT_DATA_END_TAG_OPEN=15]="SCRIPT_DATA_END_TAG_OPEN",t[t.SCRIPT_DATA_END_TAG_NAME=16]="SCRIPT_DATA_END_TAG_NAME",t[t.SCRIPT_DATA_ESCAPE_START=17]="SCRIPT_DATA_ESCAPE_START",t[t.SCRIPT_DATA_ESCAPE_START_DASH=18]="SCRIPT_DATA_ESCAPE_START_DASH",t[t.SCRIPT_DATA_ESCAPED=19]="SCRIPT_DATA_ESCAPED",t[t.SCRIPT_DATA_ESCAPED_DASH=20]="SCRIPT_DATA_ESCAPED_DASH",t[t.SCRIPT_DATA_ESCAPED_DASH_DASH=21]="SCRIPT_DATA_ESCAPED_DASH_DASH",t[t.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN=22]="SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN",t[t.SCRIPT_DATA_ESCAPED_END_TAG_OPEN=23]="SCRIPT_DATA_ESCAPED_END_TAG_OPEN",t[t.SCRIPT_DATA_ESCAPED_END_TAG_NAME=24]="SCRIPT_DATA_ESCAPED_END_TAG_NAME",t[t.SCRIPT_DATA_DOUBLE_ESCAPE_START=25]="SCRIPT_DATA_DOUBLE_ESCAPE_START",t[t.SCRIPT_DATA_DOUBLE_ESCAPED=26]="SCRIPT_DATA_DOUBLE_ESCAPED",t[t.SCRIPT_DATA_DOUBLE_ESCAPED_DASH=27]="SCRIPT_DATA_DOUBLE_ESCAPED_DASH",t[t.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH=28]="SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH",t[t.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN=29]="SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN",t[t.SCRIPT_DATA_DOUBLE_ESCAPE_END=30]="SCRIPT_DATA_DOUBLE_ESCAPE_END",t[t.BEFORE_ATTRIBUTE_NAME=31]="BEFORE_ATTRIBUTE_NAME",t[t.ATTRIBUTE_NAME=32]="ATTRIBUTE_NAME",t[t.AFTER_ATTRIBUTE_NAME=33]="AFTER_ATTRIBUTE_NAME",t[t.BEFORE_ATTRIBUTE_VALUE=34]="BEFORE_ATTRIBUTE_VALUE",t[t.ATTRIBUTE_VALUE_DOUBLE_QUOTED=35]="ATTRIBUTE_VALUE_DOUBLE_QUOTED",t[t.ATTRIBUTE_VALUE_SINGLE_QUOTED=36]="ATTRIBUTE_VALUE_SINGLE_QUOTED",t[t.ATTRIBUTE_VALUE_UNQUOTED=37]="ATTRIBUTE_VALUE_UNQUOTED",t[t.AFTER_ATTRIBUTE_VALUE_QUOTED=38]="AFTER_ATTRIBUTE_VALUE_QUOTED",t[t.SELF_CLOSING_START_TAG=39]="SELF_CLOSING_START_TAG",t[t.BOGUS_COMMENT=40]="BOGUS_COMMENT",t[t.MARKUP_DECLARATION_OPEN=41]="MARKUP_DECLARATION_OPEN",t[t.COMMENT_START=42]="COMMENT_START",t[t.COMMENT_START_DASH=43]="COMMENT_START_DASH",t[t.COMMENT=44]="COMMENT",t[t.COMMENT_LESS_THAN_SIGN=45]="COMMENT_LESS_THAN_SIGN",t[t.COMMENT_LESS_THAN_SIGN_BANG=46]="COMMENT_LESS_THAN_SIGN_BANG",t[t.COMMENT_LESS_THAN_SIGN_BANG_DASH=47]="COMMENT_LESS_THAN_SIGN_BANG_DASH",t[t.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH=48]="COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH",t[t.COMMENT_END_DASH=49]="COMMENT_END_DASH",t[t.COMMENT_END=50]="COMMENT_END",t[t.COMMENT_END_BANG=51]="COMMENT_END_BANG",t[t.DOCTYPE=52]="DOCTYPE",t[t.BEFORE_DOCTYPE_NAME=53]="BEFORE_DOCTYPE_NAME",t[t.DOCTYPE_NAME=54]="DOCTYPE_NAME",t[t.AFTER_DOCTYPE_NAME=55]="AFTER_DOCTYPE_NAME",t[t.AFTER_DOCTYPE_PUBLIC_KEYWORD=56]="AFTER_DOCTYPE_PUBLIC_KEYWORD",t[t.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER=57]="BEFORE_DOCTYPE_PUBLIC_IDENTIFIER",t[t.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED=58]="DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED",t[t.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED=59]="DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED",t[t.AFTER_DOCTYPE_PUBLIC_IDENTIFIER=60]="AFTER_DOCTYPE_PUBLIC_IDENTIFIER",t[t.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS=61]="BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS",t[t.AFTER_DOCTYPE_SYSTEM_KEYWORD=62]="AFTER_DOCTYPE_SYSTEM_KEYWORD",t[t.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER=63]="BEFORE_DOCTYPE_SYSTEM_IDENTIFIER",t[t.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED=64]="DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED",t[t.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED=65]="DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED",t[t.AFTER_DOCTYPE_SYSTEM_IDENTIFIER=66]="AFTER_DOCTYPE_SYSTEM_IDENTIFIER",t[t.BOGUS_DOCTYPE=67]="BOGUS_DOCTYPE",t[t.CDATA_SECTION=68]="CDATA_SECTION",t[t.CDATA_SECTION_BRACKET=69]="CDATA_SECTION_BRACKET",t[t.CDATA_SECTION_END=70]="CDATA_SECTION_END",t[t.CHARACTER_REFERENCE=71]="CHARACTER_REFERENCE",t[t.AMBIGUOUS_AMPERSAND=72]="AMBIGUOUS_AMPERSAND"})(B||(B={}));const _n={DATA:B.DATA,RCDATA:B.RCDATA,RAWTEXT:B.RAWTEXT,SCRIPT_DATA:B.SCRIPT_DATA,PLAINTEXT:B.PLAINTEXT,CDATA_SECTION:B.CDATA_SECTION};function w$(t){return t>=L.DIGIT_0&&t<=L.DIGIT_9}function Uu(t){return t>=L.LATIN_CAPITAL_A&&t<=L.LATIN_CAPITAL_Z}function T$(t){return t>=L.LATIN_SMALL_A&&t<=L.LATIN_SMALL_Z}function Js(t){return T$(t)||Uu(t)}function xT(t){return Js(t)||w$(t)}function bf(t){return t+32}function PN(t){return t===L.SPACE||t===L.LINE_FEED||t===L.TABULATION||t===L.FORM_FEED}function vT(t){return PN(t)||t===L.SOLIDUS||t===L.GREATER_THAN_SIGN}function S$(t){return t===L.NULL?fe.nullCharacterReference:t>1114111?fe.characterReferenceOutsideUnicodeRange:ON(t)?fe.surrogateCharacterReference:DN(t)?fe.noncharacterCharacterReference:MN(t)||t===L.CARRIAGE_RETURN?fe.controlCharacterReference:null}class _${constructor(e,n){this.options=e,this.handler=n,this.paused=!1,this.inLoop=!1,this.inForeignNode=!1,this.lastStartTagName="",this.active=!1,this.state=B.DATA,this.returnState=B.DATA,this.entityStartPos=0,this.consumedAfterSnapshot=-1,this.currentCharacterToken=null,this.currentToken=null,this.currentAttr={name:"",value:""},this.preprocessor=new c$(n),this.currentLocation=this.getCurrentLocation(-1),this.entityDecoder=new E$(d$,(r,i)=>{this.preprocessor.pos=this.entityStartPos+i-1,this._flushCodePointConsumedAsCharacterReference(r)},n.onParseError?{missingSemicolonAfterCharacterReference:()=>{this._err(fe.missingSemicolonAfterCharacterReference,1)},absenceOfDigitsInNumericCharacterReference:r=>{this._err(fe.absenceOfDigitsInNumericCharacterReference,this.entityStartPos-this.preprocessor.pos+r)},validateNumericCharacterReference:r=>{const i=S$(r);i&&this._err(i,1)}}:void 0)}_err(e,n=0){var r,i;(i=(r=this.handler).onParseError)===null||i===void 0||i.call(r,this.preprocessor.getError(e,n))}getCurrentLocation(e){return this.options.sourceCodeLocationInfo?{startLine:this.preprocessor.line,startCol:this.preprocessor.col-e,startOffset:this.preprocessor.offset-e,endLine:-1,endCol:-1,endOffset:-1}:null}_runParsingLoop(){if(!this.inLoop){for(this.inLoop=!0;this.active&&!this.paused;){this.consumedAfterSnapshot=0;const e=this._consume();this._ensureHibernation()||this._callState(e)}this.inLoop=!1}}pause(){this.paused=!0}resume(e){if(!this.paused)throw new Error("Parser was already resumed");this.paused=!1,!this.inLoop&&(this._runParsingLoop(),this.paused||e?.())}write(e,n,r){this.active=!0,this.preprocessor.write(e,n),this._runParsingLoop(),this.paused||r?.()}insertHtmlAtCurrentPos(e){this.active=!0,this.preprocessor.insertHtmlAtCurrentPos(e),this._runParsingLoop()}_ensureHibernation(){return this.preprocessor.endOfChunkHit?(this.preprocessor.retreat(this.consumedAfterSnapshot),this.consumedAfterSnapshot=0,this.active=!1,!0):!1}_consume(){return this.consumedAfterSnapshot++,this.preprocessor.advance()}_advanceBy(e){this.consumedAfterSnapshot+=e;for(let n=0;n0&&this._err(fe.endTagWithAttributes),e.selfClosing&&this._err(fe.endTagWithTrailingSolidus),this.handler.onEndTag(e)),this.preprocessor.dropParsedChunk()}emitCurrentComment(e){this.prepareToken(e),this.handler.onComment(e),this.preprocessor.dropParsedChunk()}emitCurrentDoctype(e){this.prepareToken(e),this.handler.onDoctype(e),this.preprocessor.dropParsedChunk()}_emitCurrentCharacterToken(e){if(this.currentCharacterToken){switch(e&&this.currentCharacterToken.location&&(this.currentCharacterToken.location.endLine=e.startLine,this.currentCharacterToken.location.endCol=e.startCol,this.currentCharacterToken.location.endOffset=e.startOffset),this.currentCharacterToken.type){case yt.CHARACTER:{this.handler.onCharacter(this.currentCharacterToken);break}case yt.NULL_CHARACTER:{this.handler.onNullCharacter(this.currentCharacterToken);break}case yt.WHITESPACE_CHARACTER:{this.handler.onWhitespaceCharacter(this.currentCharacterToken);break}}this.currentCharacterToken=null}}_emitEOFToken(){const e=this.getCurrentLocation(0);e&&(e.endLine=e.startLine,e.endCol=e.startCol,e.endOffset=e.startOffset),this._emitCurrentCharacterToken(e),this.handler.onEof({type:yt.EOF,location:e}),this.active=!1}_appendCharToCurrentCharacterToken(e,n){if(this.currentCharacterToken)if(this.currentCharacterToken.type===e){this.currentCharacterToken.chars+=n;return}else this.currentLocation=this.getCurrentLocation(0),this._emitCurrentCharacterToken(this.currentLocation),this.preprocessor.dropParsedChunk();this._createCharacterToken(e,n)}_emitCodePoint(e){const n=PN(e)?yt.WHITESPACE_CHARACTER:e===L.NULL?yt.NULL_CHARACTER:yt.CHARACTER;this._appendCharToCurrentCharacterToken(n,String.fromCodePoint(e))}_emitChars(e){this._appendCharToCurrentCharacterToken(yt.CHARACTER,e)}_startCharacterReference(){this.returnState=this.state,this.state=B.CHARACTER_REFERENCE,this.entityStartPos=this.preprocessor.pos,this.entityDecoder.startEntity(this._isCharacterReferenceInAttribute()?hs.Attribute:hs.Legacy)}_isCharacterReferenceInAttribute(){return this.returnState===B.ATTRIBUTE_VALUE_DOUBLE_QUOTED||this.returnState===B.ATTRIBUTE_VALUE_SINGLE_QUOTED||this.returnState===B.ATTRIBUTE_VALUE_UNQUOTED}_flushCodePointConsumedAsCharacterReference(e){this._isCharacterReferenceInAttribute()?this.currentAttr.value+=String.fromCodePoint(e):this._emitCodePoint(e)}_callState(e){switch(this.state){case B.DATA:{this._stateData(e);break}case B.RCDATA:{this._stateRcdata(e);break}case B.RAWTEXT:{this._stateRawtext(e);break}case B.SCRIPT_DATA:{this._stateScriptData(e);break}case B.PLAINTEXT:{this._statePlaintext(e);break}case B.TAG_OPEN:{this._stateTagOpen(e);break}case B.END_TAG_OPEN:{this._stateEndTagOpen(e);break}case B.TAG_NAME:{this._stateTagName(e);break}case B.RCDATA_LESS_THAN_SIGN:{this._stateRcdataLessThanSign(e);break}case B.RCDATA_END_TAG_OPEN:{this._stateRcdataEndTagOpen(e);break}case B.RCDATA_END_TAG_NAME:{this._stateRcdataEndTagName(e);break}case B.RAWTEXT_LESS_THAN_SIGN:{this._stateRawtextLessThanSign(e);break}case B.RAWTEXT_END_TAG_OPEN:{this._stateRawtextEndTagOpen(e);break}case B.RAWTEXT_END_TAG_NAME:{this._stateRawtextEndTagName(e);break}case B.SCRIPT_DATA_LESS_THAN_SIGN:{this._stateScriptDataLessThanSign(e);break}case B.SCRIPT_DATA_END_TAG_OPEN:{this._stateScriptDataEndTagOpen(e);break}case B.SCRIPT_DATA_END_TAG_NAME:{this._stateScriptDataEndTagName(e);break}case B.SCRIPT_DATA_ESCAPE_START:{this._stateScriptDataEscapeStart(e);break}case B.SCRIPT_DATA_ESCAPE_START_DASH:{this._stateScriptDataEscapeStartDash(e);break}case B.SCRIPT_DATA_ESCAPED:{this._stateScriptDataEscaped(e);break}case B.SCRIPT_DATA_ESCAPED_DASH:{this._stateScriptDataEscapedDash(e);break}case B.SCRIPT_DATA_ESCAPED_DASH_DASH:{this._stateScriptDataEscapedDashDash(e);break}case B.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:{this._stateScriptDataEscapedLessThanSign(e);break}case B.SCRIPT_DATA_ESCAPED_END_TAG_OPEN:{this._stateScriptDataEscapedEndTagOpen(e);break}case B.SCRIPT_DATA_ESCAPED_END_TAG_NAME:{this._stateScriptDataEscapedEndTagName(e);break}case B.SCRIPT_DATA_DOUBLE_ESCAPE_START:{this._stateScriptDataDoubleEscapeStart(e);break}case B.SCRIPT_DATA_DOUBLE_ESCAPED:{this._stateScriptDataDoubleEscaped(e);break}case B.SCRIPT_DATA_DOUBLE_ESCAPED_DASH:{this._stateScriptDataDoubleEscapedDash(e);break}case B.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH:{this._stateScriptDataDoubleEscapedDashDash(e);break}case B.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN:{this._stateScriptDataDoubleEscapedLessThanSign(e);break}case B.SCRIPT_DATA_DOUBLE_ESCAPE_END:{this._stateScriptDataDoubleEscapeEnd(e);break}case B.BEFORE_ATTRIBUTE_NAME:{this._stateBeforeAttributeName(e);break}case B.ATTRIBUTE_NAME:{this._stateAttributeName(e);break}case B.AFTER_ATTRIBUTE_NAME:{this._stateAfterAttributeName(e);break}case B.BEFORE_ATTRIBUTE_VALUE:{this._stateBeforeAttributeValue(e);break}case B.ATTRIBUTE_VALUE_DOUBLE_QUOTED:{this._stateAttributeValueDoubleQuoted(e);break}case B.ATTRIBUTE_VALUE_SINGLE_QUOTED:{this._stateAttributeValueSingleQuoted(e);break}case B.ATTRIBUTE_VALUE_UNQUOTED:{this._stateAttributeValueUnquoted(e);break}case B.AFTER_ATTRIBUTE_VALUE_QUOTED:{this._stateAfterAttributeValueQuoted(e);break}case B.SELF_CLOSING_START_TAG:{this._stateSelfClosingStartTag(e);break}case B.BOGUS_COMMENT:{this._stateBogusComment(e);break}case B.MARKUP_DECLARATION_OPEN:{this._stateMarkupDeclarationOpen(e);break}case B.COMMENT_START:{this._stateCommentStart(e);break}case B.COMMENT_START_DASH:{this._stateCommentStartDash(e);break}case B.COMMENT:{this._stateComment(e);break}case B.COMMENT_LESS_THAN_SIGN:{this._stateCommentLessThanSign(e);break}case B.COMMENT_LESS_THAN_SIGN_BANG:{this._stateCommentLessThanSignBang(e);break}case B.COMMENT_LESS_THAN_SIGN_BANG_DASH:{this._stateCommentLessThanSignBangDash(e);break}case B.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH:{this._stateCommentLessThanSignBangDashDash(e);break}case B.COMMENT_END_DASH:{this._stateCommentEndDash(e);break}case B.COMMENT_END:{this._stateCommentEnd(e);break}case B.COMMENT_END_BANG:{this._stateCommentEndBang(e);break}case B.DOCTYPE:{this._stateDoctype(e);break}case B.BEFORE_DOCTYPE_NAME:{this._stateBeforeDoctypeName(e);break}case B.DOCTYPE_NAME:{this._stateDoctypeName(e);break}case B.AFTER_DOCTYPE_NAME:{this._stateAfterDoctypeName(e);break}case B.AFTER_DOCTYPE_PUBLIC_KEYWORD:{this._stateAfterDoctypePublicKeyword(e);break}case B.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:{this._stateBeforeDoctypePublicIdentifier(e);break}case B.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:{this._stateDoctypePublicIdentifierDoubleQuoted(e);break}case B.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:{this._stateDoctypePublicIdentifierSingleQuoted(e);break}case B.AFTER_DOCTYPE_PUBLIC_IDENTIFIER:{this._stateAfterDoctypePublicIdentifier(e);break}case B.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:{this._stateBetweenDoctypePublicAndSystemIdentifiers(e);break}case B.AFTER_DOCTYPE_SYSTEM_KEYWORD:{this._stateAfterDoctypeSystemKeyword(e);break}case B.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:{this._stateBeforeDoctypeSystemIdentifier(e);break}case B.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:{this._stateDoctypeSystemIdentifierDoubleQuoted(e);break}case B.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:{this._stateDoctypeSystemIdentifierSingleQuoted(e);break}case B.AFTER_DOCTYPE_SYSTEM_IDENTIFIER:{this._stateAfterDoctypeSystemIdentifier(e);break}case B.BOGUS_DOCTYPE:{this._stateBogusDoctype(e);break}case B.CDATA_SECTION:{this._stateCdataSection(e);break}case B.CDATA_SECTION_BRACKET:{this._stateCdataSectionBracket(e);break}case B.CDATA_SECTION_END:{this._stateCdataSectionEnd(e);break}case B.CHARACTER_REFERENCE:{this._stateCharacterReference();break}case B.AMBIGUOUS_AMPERSAND:{this._stateAmbiguousAmpersand(e);break}default:throw new Error("Unknown state")}}_stateData(e){switch(e){case L.LESS_THAN_SIGN:{this.state=B.TAG_OPEN;break}case L.AMPERSAND:{this._startCharacterReference();break}case L.NULL:{this._err(fe.unexpectedNullCharacter),this._emitCodePoint(e);break}case L.EOF:{this._emitEOFToken();break}default:this._emitCodePoint(e)}}_stateRcdata(e){switch(e){case L.AMPERSAND:{this._startCharacterReference();break}case L.LESS_THAN_SIGN:{this.state=B.RCDATA_LESS_THAN_SIGN;break}case L.NULL:{this._err(fe.unexpectedNullCharacter),this._emitChars(cn);break}case L.EOF:{this._emitEOFToken();break}default:this._emitCodePoint(e)}}_stateRawtext(e){switch(e){case L.LESS_THAN_SIGN:{this.state=B.RAWTEXT_LESS_THAN_SIGN;break}case L.NULL:{this._err(fe.unexpectedNullCharacter),this._emitChars(cn);break}case L.EOF:{this._emitEOFToken();break}default:this._emitCodePoint(e)}}_stateScriptData(e){switch(e){case L.LESS_THAN_SIGN:{this.state=B.SCRIPT_DATA_LESS_THAN_SIGN;break}case L.NULL:{this._err(fe.unexpectedNullCharacter),this._emitChars(cn);break}case L.EOF:{this._emitEOFToken();break}default:this._emitCodePoint(e)}}_statePlaintext(e){switch(e){case L.NULL:{this._err(fe.unexpectedNullCharacter),this._emitChars(cn);break}case L.EOF:{this._emitEOFToken();break}default:this._emitCodePoint(e)}}_stateTagOpen(e){if(Js(e))this._createStartTagToken(),this.state=B.TAG_NAME,this._stateTagName(e);else switch(e){case L.EXCLAMATION_MARK:{this.state=B.MARKUP_DECLARATION_OPEN;break}case L.SOLIDUS:{this.state=B.END_TAG_OPEN;break}case L.QUESTION_MARK:{this._err(fe.unexpectedQuestionMarkInsteadOfTagName),this._createCommentToken(1),this.state=B.BOGUS_COMMENT,this._stateBogusComment(e);break}case L.EOF:{this._err(fe.eofBeforeTagName),this._emitChars("<"),this._emitEOFToken();break}default:this._err(fe.invalidFirstCharacterOfTagName),this._emitChars("<"),this.state=B.DATA,this._stateData(e)}}_stateEndTagOpen(e){if(Js(e))this._createEndTagToken(),this.state=B.TAG_NAME,this._stateTagName(e);else switch(e){case L.GREATER_THAN_SIGN:{this._err(fe.missingEndTagName),this.state=B.DATA;break}case L.EOF:{this._err(fe.eofBeforeTagName),this._emitChars("");break}case L.NULL:{this._err(fe.unexpectedNullCharacter),this.state=B.SCRIPT_DATA_ESCAPED,this._emitChars(cn);break}case L.EOF:{this._err(fe.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break}default:this.state=B.SCRIPT_DATA_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataEscapedLessThanSign(e){e===L.SOLIDUS?this.state=B.SCRIPT_DATA_ESCAPED_END_TAG_OPEN:Js(e)?(this._emitChars("<"),this.state=B.SCRIPT_DATA_DOUBLE_ESCAPE_START,this._stateScriptDataDoubleEscapeStart(e)):(this._emitChars("<"),this.state=B.SCRIPT_DATA_ESCAPED,this._stateScriptDataEscaped(e))}_stateScriptDataEscapedEndTagOpen(e){Js(e)?(this.state=B.SCRIPT_DATA_ESCAPED_END_TAG_NAME,this._stateScriptDataEscapedEndTagName(e)):(this._emitChars("");break}case L.NULL:{this._err(fe.unexpectedNullCharacter),this.state=B.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitChars(cn);break}case L.EOF:{this._err(fe.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break}default:this.state=B.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataDoubleEscapedLessThanSign(e){e===L.SOLIDUS?(this.state=B.SCRIPT_DATA_DOUBLE_ESCAPE_END,this._emitChars("/")):(this.state=B.SCRIPT_DATA_DOUBLE_ESCAPED,this._stateScriptDataDoubleEscaped(e))}_stateScriptDataDoubleEscapeEnd(e){if(this.preprocessor.startsWith(Ar.SCRIPT,!1)&&vT(this.preprocessor.peek(Ar.SCRIPT.length))){this._emitCodePoint(e);for(let n=0;n0&&this._isInTemplate()&&this.tmplCount--,this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(e,!0)}replace(e,n){const r=this._indexOf(e);this.items[r]=n,r===this.stackTop&&(this.current=n)}insertAfter(e,n,r){const i=this._indexOf(e)+1;this.items.splice(i,0,n),this.tagIDs.splice(i,0,r),this.stackTop++,i===this.stackTop&&this._updateCurrentElement(),this.current&&this.currentTagId!==void 0&&this.handler.onItemPush(this.current,this.currentTagId,i===this.stackTop)}popUntilTagNamePopped(e){let n=this.stackTop+1;do n=this.tagIDs.lastIndexOf(e,n-1);while(n>0&&this.treeAdapter.getNamespaceURI(this.items[n])!==be.HTML);this.shortenToLength(Math.max(n,0))}shortenToLength(e){for(;this.stackTop>=e;){const n=this.current;this.tmplCount>0&&this._isInTemplate()&&(this.tmplCount-=1),this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(n,this.stackTop=0;r--)if(e.has(this.tagIDs[r])&&this.treeAdapter.getNamespaceURI(this.items[r])===n)return r;return-1}clearBackTo(e,n){const r=this._indexOfTagNames(e,n);this.shortenToLength(r+1)}clearBackToTableContext(){this.clearBackTo(R$,be.HTML)}clearBackToTableBodyContext(){this.clearBackTo(N$,be.HTML)}clearBackToTableRowContext(){this.clearBackTo(k$,be.HTML)}remove(e){const n=this._indexOf(e);n>=0&&(n===this.stackTop?this.pop():(this.items.splice(n,1),this.tagIDs.splice(n,1),this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(e,!1)))}tryPeekProperlyNestedBodyElement(){return this.stackTop>=1&&this.tagIDs[1]===E.BODY?this.items[1]:null}contains(e){return this._indexOf(e)>-1}getCommonAncestor(e){const n=this._indexOf(e)-1;return n>=0?this.items[n]:null}isRootHtmlElementCurrent(){return this.stackTop===0&&this.tagIDs[0]===E.HTML}hasInDynamicScope(e,n){for(let r=this.stackTop;r>=0;r--){const i=this.tagIDs[r];switch(this.treeAdapter.getNamespaceURI(this.items[r])){case be.HTML:{if(i===e)return!0;if(n.has(i))return!1;break}case be.SVG:{if(ST.has(i))return!1;break}case be.MATHML:{if(TT.has(i))return!1;break}}}return!0}hasInScope(e){return this.hasInDynamicScope(e,ph)}hasInListItemScope(e){return this.hasInDynamicScope(e,C$)}hasInButtonScope(e){return this.hasInDynamicScope(e,A$)}hasNumberedHeaderInScope(){for(let e=this.stackTop;e>=0;e--){const n=this.tagIDs[e];switch(this.treeAdapter.getNamespaceURI(this.items[e])){case be.HTML:{if(ab.has(n))return!0;if(ph.has(n))return!1;break}case be.SVG:{if(ST.has(n))return!1;break}case be.MATHML:{if(TT.has(n))return!1;break}}}return!0}hasInTableScope(e){for(let n=this.stackTop;n>=0;n--)if(this.treeAdapter.getNamespaceURI(this.items[n])===be.HTML)switch(this.tagIDs[n]){case e:return!0;case E.TABLE:case E.HTML:return!1}return!0}hasTableBodyContextInTableScope(){for(let e=this.stackTop;e>=0;e--)if(this.treeAdapter.getNamespaceURI(this.items[e])===be.HTML)switch(this.tagIDs[e]){case E.TBODY:case E.THEAD:case E.TFOOT:return!0;case E.TABLE:case E.HTML:return!1}return!0}hasInSelectScope(e){for(let n=this.stackTop;n>=0;n--)if(this.treeAdapter.getNamespaceURI(this.items[n])===be.HTML)switch(this.tagIDs[n]){case e:return!0;case E.OPTION:case E.OPTGROUP:break;default:return!1}return!0}generateImpliedEndTags(){for(;this.currentTagId!==void 0&&FN.has(this.currentTagId);)this.pop()}generateImpliedEndTagsThoroughly(){for(;this.currentTagId!==void 0&&wT.has(this.currentTagId);)this.pop()}generateImpliedEndTagsWithExclusion(e){for(;this.currentTagId!==void 0&&this.currentTagId!==e&&wT.has(this.currentTagId);)this.pop()}}const r0=3;var zi;(function(t){t[t.Marker=0]="Marker",t[t.Element=1]="Element"})(zi||(zi={}));const _T={type:zi.Marker};class M${constructor(e){this.treeAdapter=e,this.entries=[],this.bookmark=null}_getNoahArkConditionCandidates(e,n){const r=[],i=n.length,s=this.treeAdapter.getTagName(e),o=this.treeAdapter.getNamespaceURI(e);for(let l=0;l[o.name,o.value]));let s=0;for(let o=0;oi.get(c.name)===c.value)&&(s+=1,s>=r0&&this.entries.splice(l.idx,1))}}insertMarker(){this.entries.unshift(_T)}pushElement(e,n){this._ensureNoahArkCondition(e),this.entries.unshift({type:zi.Element,element:e,token:n})}insertElementAfterBookmark(e,n){const r=this.entries.indexOf(this.bookmark);this.entries.splice(r,0,{type:zi.Element,element:e,token:n})}removeEntry(e){const n=this.entries.indexOf(e);n!==-1&&this.entries.splice(n,1)}clearToLastMarker(){const e=this.entries.indexOf(_T);e===-1?this.entries.length=0:this.entries.splice(0,e+1)}getElementEntryInScopeWithTagName(e){const n=this.entries.find(r=>r.type===zi.Marker||this.treeAdapter.getTagName(r.element)===e);return n&&n.type===zi.Element?n:null}getElementEntry(e){return this.entries.find(n=>n.type===zi.Element&&n.element===e)}}const eo={createDocument(){return{nodeName:"#document",mode:ni.NO_QUIRKS,childNodes:[]}},createDocumentFragment(){return{nodeName:"#document-fragment",childNodes:[]}},createElement(t,e,n){return{nodeName:t,tagName:t,attrs:n,namespaceURI:e,childNodes:[],parentNode:null}},createCommentNode(t){return{nodeName:"#comment",data:t,parentNode:null}},createTextNode(t){return{nodeName:"#text",value:t,parentNode:null}},appendChild(t,e){t.childNodes.push(e),e.parentNode=t},insertBefore(t,e,n){const r=t.childNodes.indexOf(n);t.childNodes.splice(r,0,e),e.parentNode=t},setTemplateContent(t,e){t.content=e},getTemplateContent(t){return t.content},setDocumentType(t,e,n,r){const i=t.childNodes.find(s=>s.nodeName==="#documentType");if(i)i.name=e,i.publicId=n,i.systemId=r;else{const s={nodeName:"#documentType",name:e,publicId:n,systemId:r,parentNode:null};eo.appendChild(t,s)}},setDocumentMode(t,e){t.mode=e},getDocumentMode(t){return t.mode},detachNode(t){if(t.parentNode){const e=t.parentNode.childNodes.indexOf(t);t.parentNode.childNodes.splice(e,1),t.parentNode=null}},insertText(t,e){if(t.childNodes.length>0){const n=t.childNodes[t.childNodes.length-1];if(eo.isTextNode(n)){n.value+=e;return}}eo.appendChild(t,eo.createTextNode(e))},insertTextBefore(t,e,n){const r=t.childNodes[t.childNodes.indexOf(n)-1];r&&eo.isTextNode(r)?r.value+=e:eo.insertBefore(t,eo.createTextNode(e),n)},adoptAttributes(t,e){const n=new Set(t.attrs.map(r=>r.name));for(let r=0;rt.startsWith(n))}function U$(t){return t.name===BN&&t.publicId===null&&(t.systemId===null||t.systemId===D$)}function H$(t){if(t.name!==BN)return ni.QUIRKS;const{systemId:e}=t;if(e&&e.toLowerCase()===L$)return ni.QUIRKS;let{publicId:n}=t;if(n!==null){if(n=n.toLowerCase(),F$.has(n))return ni.QUIRKS;let r=e===null?P$:UN;if(CT(n,r))return ni.QUIRKS;if(r=e===null?HN:B$,CT(n,r))return ni.LIMITED_QUIRKS}return ni.NO_QUIRKS}const AT={TEXT_HTML:"text/html",APPLICATION_XML:"application/xhtml+xml"},z$="definitionurl",j$="definitionURL",$$=new Map(["attributeName","attributeType","baseFrequency","baseProfile","calcMode","clipPathUnits","diffuseConstant","edgeMode","filterUnits","glyphRef","gradientTransform","gradientUnits","kernelMatrix","kernelUnitLength","keyPoints","keySplines","keyTimes","lengthAdjust","limitingConeAngle","markerHeight","markerUnits","markerWidth","maskContentUnits","maskUnits","numOctaves","pathLength","patternContentUnits","patternTransform","patternUnits","pointsAtX","pointsAtY","pointsAtZ","preserveAlpha","preserveAspectRatio","primitiveUnits","refX","refY","repeatCount","repeatDur","requiredExtensions","requiredFeatures","specularConstant","specularExponent","spreadMethod","startOffset","stdDeviation","stitchTiles","surfaceScale","systemLanguage","tableValues","targetX","targetY","textLength","viewBox","viewTarget","xChannelSelector","yChannelSelector","zoomAndPan"].map(t=>[t.toLowerCase(),t])),W$=new Map([["xlink:actuate",{prefix:"xlink",name:"actuate",namespace:be.XLINK}],["xlink:arcrole",{prefix:"xlink",name:"arcrole",namespace:be.XLINK}],["xlink:href",{prefix:"xlink",name:"href",namespace:be.XLINK}],["xlink:role",{prefix:"xlink",name:"role",namespace:be.XLINK}],["xlink:show",{prefix:"xlink",name:"show",namespace:be.XLINK}],["xlink:title",{prefix:"xlink",name:"title",namespace:be.XLINK}],["xlink:type",{prefix:"xlink",name:"type",namespace:be.XLINK}],["xml:lang",{prefix:"xml",name:"lang",namespace:be.XML}],["xml:space",{prefix:"xml",name:"space",namespace:be.XML}],["xmlns",{prefix:"",name:"xmlns",namespace:be.XMLNS}],["xmlns:xlink",{prefix:"xmlns",name:"xlink",namespace:be.XMLNS}]]),V$=new Map(["altGlyph","altGlyphDef","altGlyphItem","animateColor","animateMotion","animateTransform","clipPath","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","foreignObject","glyphRef","linearGradient","radialGradient","textPath"].map(t=>[t.toLowerCase(),t])),G$=new Set([E.B,E.BIG,E.BLOCKQUOTE,E.BODY,E.BR,E.CENTER,E.CODE,E.DD,E.DIV,E.DL,E.DT,E.EM,E.EMBED,E.H1,E.H2,E.H3,E.H4,E.H5,E.H6,E.HEAD,E.HR,E.I,E.IMG,E.LI,E.LISTING,E.MENU,E.META,E.NOBR,E.OL,E.P,E.PRE,E.RUBY,E.S,E.SMALL,E.SPAN,E.STRONG,E.STRIKE,E.SUB,E.SUP,E.TABLE,E.TT,E.U,E.UL,E.VAR]);function K$(t){const e=t.tagID;return e===E.FONT&&t.attrs.some(({name:r})=>r===Wo.COLOR||r===Wo.SIZE||r===Wo.FACE)||G$.has(e)}function zN(t){for(let e=0;e0&&this._setContextModes(e,n)}onItemPop(e,n){var r,i;if(this.options.sourceCodeLocationInfo&&this._setEndLocation(e,this.currentToken),(i=(r=this.treeAdapter).onItemPop)===null||i===void 0||i.call(r,e,this.openElements.current),n){let s,o;this.openElements.stackTop===0&&this.fragmentContext?(s=this.fragmentContext,o=this.fragmentContextID):{current:s,currentTagId:o}=this.openElements,this._setContextModes(s,o)}}_setContextModes(e,n){const r=e===this.document||e&&this.treeAdapter.getNamespaceURI(e)===be.HTML;this.currentNotInHTML=!r,this.tokenizer.inForeignNode=!r&&e!==void 0&&n!==void 0&&!this._isIntegrationPoint(n,e)}_switchToTextParsing(e,n){this._insertElement(e,be.HTML),this.tokenizer.state=n,this.originalInsertionMode=this.insertionMode,this.insertionMode=$.TEXT}switchToPlaintextParsing(){this.insertionMode=$.TEXT,this.originalInsertionMode=$.IN_BODY,this.tokenizer.state=_n.PLAINTEXT}_getAdjustedCurrentElement(){return this.openElements.stackTop===0&&this.fragmentContext?this.fragmentContext:this.openElements.current}_findFormInFragmentContext(){let e=this.fragmentContext;for(;e;){if(this.treeAdapter.getTagName(e)===re.FORM){this.formElement=e;break}e=this.treeAdapter.getParentNode(e)}}_initTokenizerForFragmentParsing(){if(!(!this.fragmentContext||this.treeAdapter.getNamespaceURI(this.fragmentContext)!==be.HTML))switch(this.fragmentContextID){case E.TITLE:case E.TEXTAREA:{this.tokenizer.state=_n.RCDATA;break}case E.STYLE:case E.XMP:case E.IFRAME:case E.NOEMBED:case E.NOFRAMES:case E.NOSCRIPT:{this.tokenizer.state=_n.RAWTEXT;break}case E.SCRIPT:{this.tokenizer.state=_n.SCRIPT_DATA;break}case E.PLAINTEXT:{this.tokenizer.state=_n.PLAINTEXT;break}}}_setDocumentType(e){const n=e.name||"",r=e.publicId||"",i=e.systemId||"";if(this.treeAdapter.setDocumentType(this.document,n,r,i),e.location){const o=this.treeAdapter.getChildNodes(this.document).find(l=>this.treeAdapter.isDocumentTypeNode(l));o&&this.treeAdapter.setNodeSourceCodeLocation(o,e.location)}}_attachElementToTree(e,n){if(this.options.sourceCodeLocationInfo){const r=n&&{...n,startTag:n};this.treeAdapter.setNodeSourceCodeLocation(e,r)}if(this._shouldFosterParentOnInsertion())this._fosterParentElement(e);else{const r=this.openElements.currentTmplContentOrNode;this.treeAdapter.appendChild(r??this.document,e)}}_appendElement(e,n){const r=this.treeAdapter.createElement(e.tagName,n,e.attrs);this._attachElementToTree(r,e.location)}_insertElement(e,n){const r=this.treeAdapter.createElement(e.tagName,n,e.attrs);this._attachElementToTree(r,e.location),this.openElements.push(r,e.tagID)}_insertFakeElement(e,n){const r=this.treeAdapter.createElement(e,be.HTML,[]);this._attachElementToTree(r,null),this.openElements.push(r,n)}_insertTemplate(e){const n=this.treeAdapter.createElement(e.tagName,be.HTML,e.attrs),r=this.treeAdapter.createDocumentFragment();this.treeAdapter.setTemplateContent(n,r),this._attachElementToTree(n,e.location),this.openElements.push(n,e.tagID),this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(r,null)}_insertFakeRootElement(){const e=this.treeAdapter.createElement(re.HTML,be.HTML,[]);this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(e,null),this.treeAdapter.appendChild(this.openElements.current,e),this.openElements.push(e,E.HTML)}_appendCommentNode(e,n){const r=this.treeAdapter.createCommentNode(e.data);this.treeAdapter.appendChild(n,r),this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(r,e.location)}_insertCharacters(e){let n,r;if(this._shouldFosterParentOnInsertion()?({parent:n,beforeElement:r}=this._findFosterParentingLocation(),r?this.treeAdapter.insertTextBefore(n,e.chars,r):this.treeAdapter.insertText(n,e.chars)):(n=this.openElements.currentTmplContentOrNode,this.treeAdapter.insertText(n,e.chars)),!e.location)return;const i=this.treeAdapter.getChildNodes(n),s=r?i.lastIndexOf(r):i.length,o=i[s-1];if(this.treeAdapter.getNodeSourceCodeLocation(o)){const{endLine:c,endCol:d,endOffset:f}=e.location;this.treeAdapter.updateNodeSourceCodeLocation(o,{endLine:c,endCol:d,endOffset:f})}else this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(o,e.location)}_adoptNodes(e,n){for(let r=this.treeAdapter.getFirstChild(e);r;r=this.treeAdapter.getFirstChild(e))this.treeAdapter.detachNode(r),this.treeAdapter.appendChild(n,r)}_setEndLocation(e,n){if(this.treeAdapter.getNodeSourceCodeLocation(e)&&n.location){const r=n.location,i=this.treeAdapter.getTagName(e),s=n.type===yt.END_TAG&&i===n.tagName?{endTag:{...r},endLine:r.endLine,endCol:r.endCol,endOffset:r.endOffset}:{endLine:r.startLine,endCol:r.startCol,endOffset:r.startOffset};this.treeAdapter.updateNodeSourceCodeLocation(e,s)}}shouldProcessStartTagTokenInForeignContent(e){if(!this.currentNotInHTML)return!1;let n,r;return this.openElements.stackTop===0&&this.fragmentContext?(n=this.fragmentContext,r=this.fragmentContextID):{current:n,currentTagId:r}=this.openElements,e.tagID===E.SVG&&this.treeAdapter.getTagName(n)===re.ANNOTATION_XML&&this.treeAdapter.getNamespaceURI(n)===be.MATHML?!1:this.tokenizer.inForeignNode||(e.tagID===E.MGLYPH||e.tagID===E.MALIGNMARK)&&r!==void 0&&!this._isIntegrationPoint(r,n,be.HTML)}_processToken(e){switch(e.type){case yt.CHARACTER:{this.onCharacter(e);break}case yt.NULL_CHARACTER:{this.onNullCharacter(e);break}case yt.COMMENT:{this.onComment(e);break}case yt.DOCTYPE:{this.onDoctype(e);break}case yt.START_TAG:{this._processStartTag(e);break}case yt.END_TAG:{this.onEndTag(e);break}case yt.EOF:{this.onEof(e);break}case yt.WHITESPACE_CHARACTER:{this.onWhitespaceCharacter(e);break}}}_isIntegrationPoint(e,n,r){const i=this.treeAdapter.getNamespaceURI(n),s=this.treeAdapter.getAttrList(n);return Q$(e,i,s,r)}_reconstructActiveFormattingElements(){const e=this.activeFormattingElements.entries.length;if(e){const n=this.activeFormattingElements.entries.findIndex(i=>i.type===zi.Marker||this.openElements.contains(i.element)),r=n===-1?e-1:n-1;for(let i=r;i>=0;i--){const s=this.activeFormattingElements.entries[i];this._insertElement(s.token,this.treeAdapter.getNamespaceURI(s.element)),s.element=this.openElements.current}}}_closeTableCell(){this.openElements.generateImpliedEndTags(),this.openElements.popUntilTableCellPopped(),this.activeFormattingElements.clearToLastMarker(),this.insertionMode=$.IN_ROW}_closePElement(){this.openElements.generateImpliedEndTagsWithExclusion(E.P),this.openElements.popUntilTagNamePopped(E.P)}_resetInsertionMode(){for(let e=this.openElements.stackTop;e>=0;e--)switch(e===0&&this.fragmentContext?this.fragmentContextID:this.openElements.tagIDs[e]){case E.TR:{this.insertionMode=$.IN_ROW;return}case E.TBODY:case E.THEAD:case E.TFOOT:{this.insertionMode=$.IN_TABLE_BODY;return}case E.CAPTION:{this.insertionMode=$.IN_CAPTION;return}case E.COLGROUP:{this.insertionMode=$.IN_COLUMN_GROUP;return}case E.TABLE:{this.insertionMode=$.IN_TABLE;return}case E.BODY:{this.insertionMode=$.IN_BODY;return}case E.FRAMESET:{this.insertionMode=$.IN_FRAMESET;return}case E.SELECT:{this._resetInsertionModeForSelect(e);return}case E.TEMPLATE:{this.insertionMode=this.tmplInsertionModeStack[0];return}case E.HTML:{this.insertionMode=this.headElement?$.AFTER_HEAD:$.BEFORE_HEAD;return}case E.TD:case E.TH:{if(e>0){this.insertionMode=$.IN_CELL;return}break}case E.HEAD:{if(e>0){this.insertionMode=$.IN_HEAD;return}break}}this.insertionMode=$.IN_BODY}_resetInsertionModeForSelect(e){if(e>0)for(let n=e-1;n>0;n--){const r=this.openElements.tagIDs[n];if(r===E.TEMPLATE)break;if(r===E.TABLE){this.insertionMode=$.IN_SELECT_IN_TABLE;return}}this.insertionMode=$.IN_SELECT}_isElementCausesFosterParenting(e){return $N.has(e)}_shouldFosterParentOnInsertion(){return this.fosterParentingEnabled&&this.openElements.currentTagId!==void 0&&this._isElementCausesFosterParenting(this.openElements.currentTagId)}_findFosterParentingLocation(){for(let e=this.openElements.stackTop;e>=0;e--){const n=this.openElements.items[e];switch(this.openElements.tagIDs[e]){case E.TEMPLATE:{if(this.treeAdapter.getNamespaceURI(n)===be.HTML)return{parent:this.treeAdapter.getTemplateContent(n),beforeElement:null};break}case E.TABLE:{const r=this.treeAdapter.getParentNode(n);return r?{parent:r,beforeElement:n}:{parent:this.openElements.items[e-1],beforeElement:null}}}}return{parent:this.openElements.items[0],beforeElement:null}}_fosterParentElement(e){const n=this._findFosterParentingLocation();n.beforeElement?this.treeAdapter.insertBefore(n.parent,e,n.beforeElement):this.treeAdapter.appendChild(n.parent,e)}_isSpecialElement(e,n){const r=this.treeAdapter.getNamespaceURI(e);return v$[r].has(n)}onCharacter(e){if(this.skipNextNewLine=!1,this.tokenizer.inForeignNode){IV(this,e);return}switch(this.insertionMode){case $.INITIAL:{Mu(this,e);break}case $.BEFORE_HTML:{qu(this,e);break}case $.BEFORE_HEAD:{Xu(this,e);break}case $.IN_HEAD:{Qu(this,e);break}case $.IN_HEAD_NO_SCRIPT:{Zu(this,e);break}case $.AFTER_HEAD:{Ju(this,e);break}case $.IN_BODY:case $.IN_CAPTION:case $.IN_CELL:case $.IN_TEMPLATE:{VN(this,e);break}case $.TEXT:case $.IN_SELECT:case $.IN_SELECT_IN_TABLE:{this._insertCharacters(e);break}case $.IN_TABLE:case $.IN_TABLE_BODY:case $.IN_ROW:{i0(this,e);break}case $.IN_TABLE_TEXT:{QN(this,e);break}case $.IN_COLUMN_GROUP:{mh(this,e);break}case $.AFTER_BODY:{gh(this,e);break}case $.AFTER_AFTER_BODY:{Hf(this,e);break}}}onNullCharacter(e){if(this.skipNextNewLine=!1,this.tokenizer.inForeignNode){RV(this,e);return}switch(this.insertionMode){case $.INITIAL:{Mu(this,e);break}case $.BEFORE_HTML:{qu(this,e);break}case $.BEFORE_HEAD:{Xu(this,e);break}case $.IN_HEAD:{Qu(this,e);break}case $.IN_HEAD_NO_SCRIPT:{Zu(this,e);break}case $.AFTER_HEAD:{Ju(this,e);break}case $.TEXT:{this._insertCharacters(e);break}case $.IN_TABLE:case $.IN_TABLE_BODY:case $.IN_ROW:{i0(this,e);break}case $.IN_COLUMN_GROUP:{mh(this,e);break}case $.AFTER_BODY:{gh(this,e);break}case $.AFTER_AFTER_BODY:{Hf(this,e);break}}}onComment(e){if(this.skipNextNewLine=!1,this.currentNotInHTML){lb(this,e);return}switch(this.insertionMode){case $.INITIAL:case $.BEFORE_HTML:case $.BEFORE_HEAD:case $.IN_HEAD:case $.IN_HEAD_NO_SCRIPT:case $.AFTER_HEAD:case $.IN_BODY:case $.IN_TABLE:case $.IN_CAPTION:case $.IN_COLUMN_GROUP:case $.IN_TABLE_BODY:case $.IN_ROW:case $.IN_CELL:case $.IN_SELECT:case $.IN_SELECT_IN_TABLE:case $.IN_TEMPLATE:case $.IN_FRAMESET:case $.AFTER_FRAMESET:{lb(this,e);break}case $.IN_TABLE_TEXT:{Du(this,e);break}case $.AFTER_BODY:{lW(this,e);break}case $.AFTER_AFTER_BODY:case $.AFTER_AFTER_FRAMESET:{uW(this,e);break}}}onDoctype(e){switch(this.skipNextNewLine=!1,this.insertionMode){case $.INITIAL:{cW(this,e);break}case $.BEFORE_HEAD:case $.IN_HEAD:case $.IN_HEAD_NO_SCRIPT:case $.AFTER_HEAD:{this._err(e,fe.misplacedDoctype);break}case $.IN_TABLE_TEXT:{Du(this,e);break}}}onStartTag(e){this.skipNextNewLine=!1,this.currentToken=e,this._processStartTag(e),e.selfClosing&&!e.ackSelfClosing&&this._err(e,fe.nonVoidHtmlElementStartTagWithTrailingSolidus)}_processStartTag(e){this.shouldProcessStartTagTokenInForeignContent(e)?OV(this,e):this._startTagOutsideForeignContent(e)}_startTagOutsideForeignContent(e){switch(this.insertionMode){case $.INITIAL:{Mu(this,e);break}case $.BEFORE_HTML:{dW(this,e);break}case $.BEFORE_HEAD:{hW(this,e);break}case $.IN_HEAD:{Ni(this,e);break}case $.IN_HEAD_NO_SCRIPT:{gW(this,e);break}case $.AFTER_HEAD:{EW(this,e);break}case $.IN_BODY:{cr(this,e);break}case $.IN_TABLE:{yl(this,e);break}case $.IN_TABLE_TEXT:{Du(this,e);break}case $.IN_CAPTION:{pV(this,e);break}case $.IN_COLUMN_GROUP:{f1(this,e);break}case $.IN_TABLE_BODY:{hp(this,e);break}case $.IN_ROW:{pp(this,e);break}case $.IN_CELL:{bV(this,e);break}case $.IN_SELECT:{e2(this,e);break}case $.IN_SELECT_IN_TABLE:{yV(this,e);break}case $.IN_TEMPLATE:{vV(this,e);break}case $.AFTER_BODY:{TV(this,e);break}case $.IN_FRAMESET:{SV(this,e);break}case $.AFTER_FRAMESET:{CV(this,e);break}case $.AFTER_AFTER_BODY:{kV(this,e);break}case $.AFTER_AFTER_FRAMESET:{NV(this,e);break}}}onEndTag(e){this.skipNextNewLine=!1,this.currentToken=e,this.currentNotInHTML?MV(this,e):this._endTagOutsideForeignContent(e)}_endTagOutsideForeignContent(e){switch(this.insertionMode){case $.INITIAL:{Mu(this,e);break}case $.BEFORE_HTML:{fW(this,e);break}case $.BEFORE_HEAD:{pW(this,e);break}case $.IN_HEAD:{mW(this,e);break}case $.IN_HEAD_NO_SCRIPT:{bW(this,e);break}case $.AFTER_HEAD:{yW(this,e);break}case $.IN_BODY:{fp(this,e);break}case $.TEXT:{iV(this,e);break}case $.IN_TABLE:{mc(this,e);break}case $.IN_TABLE_TEXT:{Du(this,e);break}case $.IN_CAPTION:{mV(this,e);break}case $.IN_COLUMN_GROUP:{gV(this,e);break}case $.IN_TABLE_BODY:{ub(this,e);break}case $.IN_ROW:{JN(this,e);break}case $.IN_CELL:{EV(this,e);break}case $.IN_SELECT:{t2(this,e);break}case $.IN_SELECT_IN_TABLE:{xV(this,e);break}case $.IN_TEMPLATE:{wV(this,e);break}case $.AFTER_BODY:{r2(this,e);break}case $.IN_FRAMESET:{_V(this,e);break}case $.AFTER_FRAMESET:{AV(this,e);break}case $.AFTER_AFTER_BODY:{Hf(this,e);break}}}onEof(e){switch(this.insertionMode){case $.INITIAL:{Mu(this,e);break}case $.BEFORE_HTML:{qu(this,e);break}case $.BEFORE_HEAD:{Xu(this,e);break}case $.IN_HEAD:{Qu(this,e);break}case $.IN_HEAD_NO_SCRIPT:{Zu(this,e);break}case $.AFTER_HEAD:{Ju(this,e);break}case $.IN_BODY:case $.IN_TABLE:case $.IN_CAPTION:case $.IN_COLUMN_GROUP:case $.IN_TABLE_BODY:case $.IN_ROW:case $.IN_CELL:case $.IN_SELECT:case $.IN_SELECT_IN_TABLE:{qN(this,e);break}case $.TEXT:{sV(this,e);break}case $.IN_TABLE_TEXT:{Du(this,e);break}case $.IN_TEMPLATE:{n2(this,e);break}case $.AFTER_BODY:case $.IN_FRAMESET:case $.AFTER_FRAMESET:case $.AFTER_AFTER_BODY:case $.AFTER_AFTER_FRAMESET:{d1(this,e);break}}}onWhitespaceCharacter(e){if(this.skipNextNewLine&&(this.skipNextNewLine=!1,e.chars.charCodeAt(0)===L.LINE_FEED)){if(e.chars.length===1)return;e.chars=e.chars.substr(1)}if(this.tokenizer.inForeignNode){this._insertCharacters(e);return}switch(this.insertionMode){case $.IN_HEAD:case $.IN_HEAD_NO_SCRIPT:case $.AFTER_HEAD:case $.TEXT:case $.IN_COLUMN_GROUP:case $.IN_SELECT:case $.IN_SELECT_IN_TABLE:case $.IN_FRAMESET:case $.AFTER_FRAMESET:{this._insertCharacters(e);break}case $.IN_BODY:case $.IN_CAPTION:case $.IN_CELL:case $.IN_TEMPLATE:case $.AFTER_BODY:case $.AFTER_AFTER_BODY:case $.AFTER_AFTER_FRAMESET:{WN(this,e);break}case $.IN_TABLE:case $.IN_TABLE_BODY:case $.IN_ROW:{i0(this,e);break}case $.IN_TABLE_TEXT:{XN(this,e);break}}}}function nW(t,e){let n=t.activeFormattingElements.getElementEntryInScopeWithTagName(e.tagName);return n?t.openElements.contains(n.element)?t.openElements.hasInScope(e.tagID)||(n=null):(t.activeFormattingElements.removeEntry(n),n=null):YN(t,e),n}function rW(t,e){let n=null,r=t.openElements.stackTop;for(;r>=0;r--){const i=t.openElements.items[r];if(i===e.element)break;t._isSpecialElement(i,t.openElements.tagIDs[r])&&(n=i)}return n||(t.openElements.shortenToLength(Math.max(r,0)),t.activeFormattingElements.removeEntry(e)),n}function iW(t,e,n){let r=e,i=t.openElements.getCommonAncestor(e);for(let s=0,o=i;o!==n;s++,o=i){i=t.openElements.getCommonAncestor(o);const l=t.activeFormattingElements.getElementEntry(o),c=l&&s>=eW;!l||c?(c&&t.activeFormattingElements.removeEntry(l),t.openElements.remove(o)):(o=sW(t,l),r===e&&(t.activeFormattingElements.bookmark=l),t.treeAdapter.detachNode(r),t.treeAdapter.appendChild(o,r),r=o)}return r}function sW(t,e){const n=t.treeAdapter.getNamespaceURI(e.element),r=t.treeAdapter.createElement(e.token.tagName,n,e.token.attrs);return t.openElements.replace(e.element,r),e.element=r,r}function oW(t,e,n){const r=t.treeAdapter.getTagName(e),i=Fl(r);if(t._isElementCausesFosterParenting(i))t._fosterParentElement(n);else{const s=t.treeAdapter.getNamespaceURI(e);i===E.TEMPLATE&&s===be.HTML&&(e=t.treeAdapter.getTemplateContent(e)),t.treeAdapter.appendChild(e,n)}}function aW(t,e,n){const r=t.treeAdapter.getNamespaceURI(n.element),{token:i}=n,s=t.treeAdapter.createElement(i.tagName,r,i.attrs);t._adoptNodes(e,s),t.treeAdapter.appendChild(e,s),t.activeFormattingElements.insertElementAfterBookmark(s,i),t.activeFormattingElements.removeEntry(n),t.openElements.remove(n.element),t.openElements.insertAfter(e,s,i.tagID)}function c1(t,e){for(let n=0;n=n;r--)t._setEndLocation(t.openElements.items[r],e);if(!t.fragmentContext&&t.openElements.stackTop>=0){const r=t.openElements.items[0],i=t.treeAdapter.getNodeSourceCodeLocation(r);if(i&&!i.endTag&&(t._setEndLocation(r,e),t.openElements.stackTop>=1)){const s=t.openElements.items[1],o=t.treeAdapter.getNodeSourceCodeLocation(s);o&&!o.endTag&&t._setEndLocation(s,e)}}}}function cW(t,e){t._setDocumentType(e);const n=e.forceQuirks?ni.QUIRKS:H$(e);U$(e)||t._err(e,fe.nonConformingDoctype),t.treeAdapter.setDocumentMode(t.document,n),t.insertionMode=$.BEFORE_HTML}function Mu(t,e){t._err(e,fe.missingDoctype,!0),t.treeAdapter.setDocumentMode(t.document,ni.QUIRKS),t.insertionMode=$.BEFORE_HTML,t._processToken(e)}function dW(t,e){e.tagID===E.HTML?(t._insertElement(e,be.HTML),t.insertionMode=$.BEFORE_HEAD):qu(t,e)}function fW(t,e){const n=e.tagID;(n===E.HTML||n===E.HEAD||n===E.BODY||n===E.BR)&&qu(t,e)}function qu(t,e){t._insertFakeRootElement(),t.insertionMode=$.BEFORE_HEAD,t._processToken(e)}function hW(t,e){switch(e.tagID){case E.HTML:{cr(t,e);break}case E.HEAD:{t._insertElement(e,be.HTML),t.headElement=t.openElements.current,t.insertionMode=$.IN_HEAD;break}default:Xu(t,e)}}function pW(t,e){const n=e.tagID;n===E.HEAD||n===E.BODY||n===E.HTML||n===E.BR?Xu(t,e):t._err(e,fe.endTagWithoutMatchingOpenElement)}function Xu(t,e){t._insertFakeElement(re.HEAD,E.HEAD),t.headElement=t.openElements.current,t.insertionMode=$.IN_HEAD,t._processToken(e)}function Ni(t,e){switch(e.tagID){case E.HTML:{cr(t,e);break}case E.BASE:case E.BASEFONT:case E.BGSOUND:case E.LINK:case E.META:{t._appendElement(e,be.HTML),e.ackSelfClosing=!0;break}case E.TITLE:{t._switchToTextParsing(e,_n.RCDATA);break}case E.NOSCRIPT:{t.options.scriptingEnabled?t._switchToTextParsing(e,_n.RAWTEXT):(t._insertElement(e,be.HTML),t.insertionMode=$.IN_HEAD_NO_SCRIPT);break}case E.NOFRAMES:case E.STYLE:{t._switchToTextParsing(e,_n.RAWTEXT);break}case E.SCRIPT:{t._switchToTextParsing(e,_n.SCRIPT_DATA);break}case E.TEMPLATE:{t._insertTemplate(e),t.activeFormattingElements.insertMarker(),t.framesetOk=!1,t.insertionMode=$.IN_TEMPLATE,t.tmplInsertionModeStack.unshift($.IN_TEMPLATE);break}case E.HEAD:{t._err(e,fe.misplacedStartTagForHeadElement);break}default:Qu(t,e)}}function mW(t,e){switch(e.tagID){case E.HEAD:{t.openElements.pop(),t.insertionMode=$.AFTER_HEAD;break}case E.BODY:case E.BR:case E.HTML:{Qu(t,e);break}case E.TEMPLATE:{ca(t,e);break}default:t._err(e,fe.endTagWithoutMatchingOpenElement)}}function ca(t,e){t.openElements.tmplCount>0?(t.openElements.generateImpliedEndTagsThoroughly(),t.openElements.currentTagId!==E.TEMPLATE&&t._err(e,fe.closingOfElementWithOpenChildElements),t.openElements.popUntilTagNamePopped(E.TEMPLATE),t.activeFormattingElements.clearToLastMarker(),t.tmplInsertionModeStack.shift(),t._resetInsertionMode()):t._err(e,fe.endTagWithoutMatchingOpenElement)}function Qu(t,e){t.openElements.pop(),t.insertionMode=$.AFTER_HEAD,t._processToken(e)}function gW(t,e){switch(e.tagID){case E.HTML:{cr(t,e);break}case E.BASEFONT:case E.BGSOUND:case E.HEAD:case E.LINK:case E.META:case E.NOFRAMES:case E.STYLE:{Ni(t,e);break}case E.NOSCRIPT:{t._err(e,fe.nestedNoscriptInHead);break}default:Zu(t,e)}}function bW(t,e){switch(e.tagID){case E.NOSCRIPT:{t.openElements.pop(),t.insertionMode=$.IN_HEAD;break}case E.BR:{Zu(t,e);break}default:t._err(e,fe.endTagWithoutMatchingOpenElement)}}function Zu(t,e){const n=e.type===yt.EOF?fe.openElementsLeftAfterEof:fe.disallowedContentInNoscriptInHead;t._err(e,n),t.openElements.pop(),t.insertionMode=$.IN_HEAD,t._processToken(e)}function EW(t,e){switch(e.tagID){case E.HTML:{cr(t,e);break}case E.BODY:{t._insertElement(e,be.HTML),t.framesetOk=!1,t.insertionMode=$.IN_BODY;break}case E.FRAMESET:{t._insertElement(e,be.HTML),t.insertionMode=$.IN_FRAMESET;break}case E.BASE:case E.BASEFONT:case E.BGSOUND:case E.LINK:case E.META:case E.NOFRAMES:case E.SCRIPT:case E.STYLE:case E.TEMPLATE:case E.TITLE:{t._err(e,fe.abandonedHeadElementChild),t.openElements.push(t.headElement,E.HEAD),Ni(t,e),t.openElements.remove(t.headElement);break}case E.HEAD:{t._err(e,fe.misplacedStartTagForHeadElement);break}default:Ju(t,e)}}function yW(t,e){switch(e.tagID){case E.BODY:case E.HTML:case E.BR:{Ju(t,e);break}case E.TEMPLATE:{ca(t,e);break}default:t._err(e,fe.endTagWithoutMatchingOpenElement)}}function Ju(t,e){t._insertFakeElement(re.BODY,E.BODY),t.insertionMode=$.IN_BODY,dp(t,e)}function dp(t,e){switch(e.type){case yt.CHARACTER:{VN(t,e);break}case yt.WHITESPACE_CHARACTER:{WN(t,e);break}case yt.COMMENT:{lb(t,e);break}case yt.START_TAG:{cr(t,e);break}case yt.END_TAG:{fp(t,e);break}case yt.EOF:{qN(t,e);break}}}function WN(t,e){t._reconstructActiveFormattingElements(),t._insertCharacters(e)}function VN(t,e){t._reconstructActiveFormattingElements(),t._insertCharacters(e),t.framesetOk=!1}function xW(t,e){t.openElements.tmplCount===0&&t.treeAdapter.adoptAttributes(t.openElements.items[0],e.attrs)}function vW(t,e){const n=t.openElements.tryPeekProperlyNestedBodyElement();n&&t.openElements.tmplCount===0&&(t.framesetOk=!1,t.treeAdapter.adoptAttributes(n,e.attrs))}function wW(t,e){const n=t.openElements.tryPeekProperlyNestedBodyElement();t.framesetOk&&n&&(t.treeAdapter.detachNode(n),t.openElements.popAllUpToHtmlElement(),t._insertElement(e,be.HTML),t.insertionMode=$.IN_FRAMESET)}function TW(t,e){t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._insertElement(e,be.HTML)}function SW(t,e){t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t.openElements.currentTagId!==void 0&&ab.has(t.openElements.currentTagId)&&t.openElements.pop(),t._insertElement(e,be.HTML)}function _W(t,e){t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._insertElement(e,be.HTML),t.skipNextNewLine=!0,t.framesetOk=!1}function CW(t,e){const n=t.openElements.tmplCount>0;(!t.formElement||n)&&(t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._insertElement(e,be.HTML),n||(t.formElement=t.openElements.current))}function AW(t,e){t.framesetOk=!1;const n=e.tagID;for(let r=t.openElements.stackTop;r>=0;r--){const i=t.openElements.tagIDs[r];if(n===E.LI&&i===E.LI||(n===E.DD||n===E.DT)&&(i===E.DD||i===E.DT)){t.openElements.generateImpliedEndTagsWithExclusion(i),t.openElements.popUntilTagNamePopped(i);break}if(i!==E.ADDRESS&&i!==E.DIV&&i!==E.P&&t._isSpecialElement(t.openElements.items[r],i))break}t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._insertElement(e,be.HTML)}function kW(t,e){t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._insertElement(e,be.HTML),t.tokenizer.state=_n.PLAINTEXT}function NW(t,e){t.openElements.hasInScope(E.BUTTON)&&(t.openElements.generateImpliedEndTags(),t.openElements.popUntilTagNamePopped(E.BUTTON)),t._reconstructActiveFormattingElements(),t._insertElement(e,be.HTML),t.framesetOk=!1}function RW(t,e){const n=t.activeFormattingElements.getElementEntryInScopeWithTagName(re.A);n&&(c1(t,e),t.openElements.remove(n.element),t.activeFormattingElements.removeEntry(n)),t._reconstructActiveFormattingElements(),t._insertElement(e,be.HTML),t.activeFormattingElements.pushElement(t.openElements.current,e)}function IW(t,e){t._reconstructActiveFormattingElements(),t._insertElement(e,be.HTML),t.activeFormattingElements.pushElement(t.openElements.current,e)}function OW(t,e){t._reconstructActiveFormattingElements(),t.openElements.hasInScope(E.NOBR)&&(c1(t,e),t._reconstructActiveFormattingElements()),t._insertElement(e,be.HTML),t.activeFormattingElements.pushElement(t.openElements.current,e)}function MW(t,e){t._reconstructActiveFormattingElements(),t._insertElement(e,be.HTML),t.activeFormattingElements.insertMarker(),t.framesetOk=!1}function DW(t,e){t.treeAdapter.getDocumentMode(t.document)!==ni.QUIRKS&&t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._insertElement(e,be.HTML),t.framesetOk=!1,t.insertionMode=$.IN_TABLE}function GN(t,e){t._reconstructActiveFormattingElements(),t._appendElement(e,be.HTML),t.framesetOk=!1,e.ackSelfClosing=!0}function KN(t){const e=LN(t,Wo.TYPE);return e!=null&&e.toLowerCase()===Z$}function LW(t,e){t._reconstructActiveFormattingElements(),t._appendElement(e,be.HTML),KN(e)||(t.framesetOk=!1),e.ackSelfClosing=!0}function PW(t,e){t._appendElement(e,be.HTML),e.ackSelfClosing=!0}function FW(t,e){t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._appendElement(e,be.HTML),t.framesetOk=!1,e.ackSelfClosing=!0}function BW(t,e){e.tagName=re.IMG,e.tagID=E.IMG,GN(t,e)}function UW(t,e){t._insertElement(e,be.HTML),t.skipNextNewLine=!0,t.tokenizer.state=_n.RCDATA,t.originalInsertionMode=t.insertionMode,t.framesetOk=!1,t.insertionMode=$.TEXT}function HW(t,e){t.openElements.hasInButtonScope(E.P)&&t._closePElement(),t._reconstructActiveFormattingElements(),t.framesetOk=!1,t._switchToTextParsing(e,_n.RAWTEXT)}function zW(t,e){t.framesetOk=!1,t._switchToTextParsing(e,_n.RAWTEXT)}function RT(t,e){t._switchToTextParsing(e,_n.RAWTEXT)}function jW(t,e){t._reconstructActiveFormattingElements(),t._insertElement(e,be.HTML),t.framesetOk=!1,t.insertionMode=t.insertionMode===$.IN_TABLE||t.insertionMode===$.IN_CAPTION||t.insertionMode===$.IN_TABLE_BODY||t.insertionMode===$.IN_ROW||t.insertionMode===$.IN_CELL?$.IN_SELECT_IN_TABLE:$.IN_SELECT}function $W(t,e){t.openElements.currentTagId===E.OPTION&&t.openElements.pop(),t._reconstructActiveFormattingElements(),t._insertElement(e,be.HTML)}function WW(t,e){t.openElements.hasInScope(E.RUBY)&&t.openElements.generateImpliedEndTags(),t._insertElement(e,be.HTML)}function VW(t,e){t.openElements.hasInScope(E.RUBY)&&t.openElements.generateImpliedEndTagsWithExclusion(E.RTC),t._insertElement(e,be.HTML)}function GW(t,e){t._reconstructActiveFormattingElements(),zN(e),u1(e),e.selfClosing?t._appendElement(e,be.MATHML):t._insertElement(e,be.MATHML),e.ackSelfClosing=!0}function KW(t,e){t._reconstructActiveFormattingElements(),jN(e),u1(e),e.selfClosing?t._appendElement(e,be.SVG):t._insertElement(e,be.SVG),e.ackSelfClosing=!0}function IT(t,e){t._reconstructActiveFormattingElements(),t._insertElement(e,be.HTML)}function cr(t,e){switch(e.tagID){case E.I:case E.S:case E.B:case E.U:case E.EM:case E.TT:case E.BIG:case E.CODE:case E.FONT:case E.SMALL:case E.STRIKE:case E.STRONG:{IW(t,e);break}case E.A:{RW(t,e);break}case E.H1:case E.H2:case E.H3:case E.H4:case E.H5:case E.H6:{SW(t,e);break}case E.P:case E.DL:case E.OL:case E.UL:case E.DIV:case E.DIR:case E.NAV:case E.MAIN:case E.MENU:case E.ASIDE:case E.CENTER:case E.FIGURE:case E.FOOTER:case E.HEADER:case E.HGROUP:case E.DIALOG:case E.DETAILS:case E.ADDRESS:case E.ARTICLE:case E.SEARCH:case E.SECTION:case E.SUMMARY:case E.FIELDSET:case E.BLOCKQUOTE:case E.FIGCAPTION:{TW(t,e);break}case E.LI:case E.DD:case E.DT:{AW(t,e);break}case E.BR:case E.IMG:case E.WBR:case E.AREA:case E.EMBED:case E.KEYGEN:{GN(t,e);break}case E.HR:{FW(t,e);break}case E.RB:case E.RTC:{WW(t,e);break}case E.RT:case E.RP:{VW(t,e);break}case E.PRE:case E.LISTING:{_W(t,e);break}case E.XMP:{HW(t,e);break}case E.SVG:{KW(t,e);break}case E.HTML:{xW(t,e);break}case E.BASE:case E.LINK:case E.META:case E.STYLE:case E.TITLE:case E.SCRIPT:case E.BGSOUND:case E.BASEFONT:case E.TEMPLATE:{Ni(t,e);break}case E.BODY:{vW(t,e);break}case E.FORM:{CW(t,e);break}case E.NOBR:{OW(t,e);break}case E.MATH:{GW(t,e);break}case E.TABLE:{DW(t,e);break}case E.INPUT:{LW(t,e);break}case E.PARAM:case E.TRACK:case E.SOURCE:{PW(t,e);break}case E.IMAGE:{BW(t,e);break}case E.BUTTON:{NW(t,e);break}case E.APPLET:case E.OBJECT:case E.MARQUEE:{MW(t,e);break}case E.IFRAME:{zW(t,e);break}case E.SELECT:{jW(t,e);break}case E.OPTION:case E.OPTGROUP:{$W(t,e);break}case E.NOEMBED:case E.NOFRAMES:{RT(t,e);break}case E.FRAMESET:{wW(t,e);break}case E.TEXTAREA:{UW(t,e);break}case E.NOSCRIPT:{t.options.scriptingEnabled?RT(t,e):IT(t,e);break}case E.PLAINTEXT:{kW(t,e);break}case E.COL:case E.TH:case E.TD:case E.TR:case E.HEAD:case E.FRAME:case E.TBODY:case E.TFOOT:case E.THEAD:case E.CAPTION:case E.COLGROUP:break;default:IT(t,e)}}function YW(t,e){if(t.openElements.hasInScope(E.BODY)&&(t.insertionMode=$.AFTER_BODY,t.options.sourceCodeLocationInfo)){const n=t.openElements.tryPeekProperlyNestedBodyElement();n&&t._setEndLocation(n,e)}}function qW(t,e){t.openElements.hasInScope(E.BODY)&&(t.insertionMode=$.AFTER_BODY,r2(t,e))}function XW(t,e){const n=e.tagID;t.openElements.hasInScope(n)&&(t.openElements.generateImpliedEndTags(),t.openElements.popUntilTagNamePopped(n))}function QW(t){const e=t.openElements.tmplCount>0,{formElement:n}=t;e||(t.formElement=null),(n||e)&&t.openElements.hasInScope(E.FORM)&&(t.openElements.generateImpliedEndTags(),e?t.openElements.popUntilTagNamePopped(E.FORM):n&&t.openElements.remove(n))}function ZW(t){t.openElements.hasInButtonScope(E.P)||t._insertFakeElement(re.P,E.P),t._closePElement()}function JW(t){t.openElements.hasInListItemScope(E.LI)&&(t.openElements.generateImpliedEndTagsWithExclusion(E.LI),t.openElements.popUntilTagNamePopped(E.LI))}function eV(t,e){const n=e.tagID;t.openElements.hasInScope(n)&&(t.openElements.generateImpliedEndTagsWithExclusion(n),t.openElements.popUntilTagNamePopped(n))}function tV(t){t.openElements.hasNumberedHeaderInScope()&&(t.openElements.generateImpliedEndTags(),t.openElements.popUntilNumberedHeaderPopped())}function nV(t,e){const n=e.tagID;t.openElements.hasInScope(n)&&(t.openElements.generateImpliedEndTags(),t.openElements.popUntilTagNamePopped(n),t.activeFormattingElements.clearToLastMarker())}function rV(t){t._reconstructActiveFormattingElements(),t._insertFakeElement(re.BR,E.BR),t.openElements.pop(),t.framesetOk=!1}function YN(t,e){const n=e.tagName,r=e.tagID;for(let i=t.openElements.stackTop;i>0;i--){const s=t.openElements.items[i],o=t.openElements.tagIDs[i];if(r===o&&(r!==E.UNKNOWN||t.treeAdapter.getTagName(s)===n)){t.openElements.generateImpliedEndTagsWithExclusion(r),t.openElements.stackTop>=i&&t.openElements.shortenToLength(i);break}if(t._isSpecialElement(s,o))break}}function fp(t,e){switch(e.tagID){case E.A:case E.B:case E.I:case E.S:case E.U:case E.EM:case E.TT:case E.BIG:case E.CODE:case E.FONT:case E.NOBR:case E.SMALL:case E.STRIKE:case E.STRONG:{c1(t,e);break}case E.P:{ZW(t);break}case E.DL:case E.UL:case E.OL:case E.DIR:case E.DIV:case E.NAV:case E.PRE:case E.MAIN:case E.MENU:case E.ASIDE:case E.BUTTON:case E.CENTER:case E.FIGURE:case E.FOOTER:case E.HEADER:case E.HGROUP:case E.DIALOG:case E.ADDRESS:case E.ARTICLE:case E.DETAILS:case E.SEARCH:case E.SECTION:case E.SUMMARY:case E.LISTING:case E.FIELDSET:case E.BLOCKQUOTE:case E.FIGCAPTION:{XW(t,e);break}case E.LI:{JW(t);break}case E.DD:case E.DT:{eV(t,e);break}case E.H1:case E.H2:case E.H3:case E.H4:case E.H5:case E.H6:{tV(t);break}case E.BR:{rV(t);break}case E.BODY:{YW(t,e);break}case E.HTML:{qW(t,e);break}case E.FORM:{QW(t);break}case E.APPLET:case E.OBJECT:case E.MARQUEE:{nV(t,e);break}case E.TEMPLATE:{ca(t,e);break}default:YN(t,e)}}function qN(t,e){t.tmplInsertionModeStack.length>0?n2(t,e):d1(t,e)}function iV(t,e){var n;e.tagID===E.SCRIPT&&((n=t.scriptHandler)===null||n===void 0||n.call(t,t.openElements.current)),t.openElements.pop(),t.insertionMode=t.originalInsertionMode}function sV(t,e){t._err(e,fe.eofInElementThatCanContainOnlyText),t.openElements.pop(),t.insertionMode=t.originalInsertionMode,t.onEof(e)}function i0(t,e){if(t.openElements.currentTagId!==void 0&&$N.has(t.openElements.currentTagId))switch(t.pendingCharacterTokens.length=0,t.hasNonWhitespacePendingCharacterToken=!1,t.originalInsertionMode=t.insertionMode,t.insertionMode=$.IN_TABLE_TEXT,e.type){case yt.CHARACTER:{QN(t,e);break}case yt.WHITESPACE_CHARACTER:{XN(t,e);break}}else Uc(t,e)}function oV(t,e){t.openElements.clearBackToTableContext(),t.activeFormattingElements.insertMarker(),t._insertElement(e,be.HTML),t.insertionMode=$.IN_CAPTION}function aV(t,e){t.openElements.clearBackToTableContext(),t._insertElement(e,be.HTML),t.insertionMode=$.IN_COLUMN_GROUP}function lV(t,e){t.openElements.clearBackToTableContext(),t._insertFakeElement(re.COLGROUP,E.COLGROUP),t.insertionMode=$.IN_COLUMN_GROUP,f1(t,e)}function uV(t,e){t.openElements.clearBackToTableContext(),t._insertElement(e,be.HTML),t.insertionMode=$.IN_TABLE_BODY}function cV(t,e){t.openElements.clearBackToTableContext(),t._insertFakeElement(re.TBODY,E.TBODY),t.insertionMode=$.IN_TABLE_BODY,hp(t,e)}function dV(t,e){t.openElements.hasInTableScope(E.TABLE)&&(t.openElements.popUntilTagNamePopped(E.TABLE),t._resetInsertionMode(),t._processStartTag(e))}function fV(t,e){KN(e)?t._appendElement(e,be.HTML):Uc(t,e),e.ackSelfClosing=!0}function hV(t,e){!t.formElement&&t.openElements.tmplCount===0&&(t._insertElement(e,be.HTML),t.formElement=t.openElements.current,t.openElements.pop())}function yl(t,e){switch(e.tagID){case E.TD:case E.TH:case E.TR:{cV(t,e);break}case E.STYLE:case E.SCRIPT:case E.TEMPLATE:{Ni(t,e);break}case E.COL:{lV(t,e);break}case E.FORM:{hV(t,e);break}case E.TABLE:{dV(t,e);break}case E.TBODY:case E.TFOOT:case E.THEAD:{uV(t,e);break}case E.INPUT:{fV(t,e);break}case E.CAPTION:{oV(t,e);break}case E.COLGROUP:{aV(t,e);break}default:Uc(t,e)}}function mc(t,e){switch(e.tagID){case E.TABLE:{t.openElements.hasInTableScope(E.TABLE)&&(t.openElements.popUntilTagNamePopped(E.TABLE),t._resetInsertionMode());break}case E.TEMPLATE:{ca(t,e);break}case E.BODY:case E.CAPTION:case E.COL:case E.COLGROUP:case E.HTML:case E.TBODY:case E.TD:case E.TFOOT:case E.TH:case E.THEAD:case E.TR:break;default:Uc(t,e)}}function Uc(t,e){const n=t.fosterParentingEnabled;t.fosterParentingEnabled=!0,dp(t,e),t.fosterParentingEnabled=n}function XN(t,e){t.pendingCharacterTokens.push(e)}function QN(t,e){t.pendingCharacterTokens.push(e),t.hasNonWhitespacePendingCharacterToken=!0}function Du(t,e){let n=0;if(t.hasNonWhitespacePendingCharacterToken)for(;n0&&t.openElements.currentTagId===E.OPTION&&t.openElements.tagIDs[t.openElements.stackTop-1]===E.OPTGROUP&&t.openElements.pop(),t.openElements.currentTagId===E.OPTGROUP&&t.openElements.pop();break}case E.OPTION:{t.openElements.currentTagId===E.OPTION&&t.openElements.pop();break}case E.SELECT:{t.openElements.hasInSelectScope(E.SELECT)&&(t.openElements.popUntilTagNamePopped(E.SELECT),t._resetInsertionMode());break}case E.TEMPLATE:{ca(t,e);break}}}function yV(t,e){const n=e.tagID;n===E.CAPTION||n===E.TABLE||n===E.TBODY||n===E.TFOOT||n===E.THEAD||n===E.TR||n===E.TD||n===E.TH?(t.openElements.popUntilTagNamePopped(E.SELECT),t._resetInsertionMode(),t._processStartTag(e)):e2(t,e)}function xV(t,e){const n=e.tagID;n===E.CAPTION||n===E.TABLE||n===E.TBODY||n===E.TFOOT||n===E.THEAD||n===E.TR||n===E.TD||n===E.TH?t.openElements.hasInTableScope(n)&&(t.openElements.popUntilTagNamePopped(E.SELECT),t._resetInsertionMode(),t.onEndTag(e)):t2(t,e)}function vV(t,e){switch(e.tagID){case E.BASE:case E.BASEFONT:case E.BGSOUND:case E.LINK:case E.META:case E.NOFRAMES:case E.SCRIPT:case E.STYLE:case E.TEMPLATE:case E.TITLE:{Ni(t,e);break}case E.CAPTION:case E.COLGROUP:case E.TBODY:case E.TFOOT:case E.THEAD:{t.tmplInsertionModeStack[0]=$.IN_TABLE,t.insertionMode=$.IN_TABLE,yl(t,e);break}case E.COL:{t.tmplInsertionModeStack[0]=$.IN_COLUMN_GROUP,t.insertionMode=$.IN_COLUMN_GROUP,f1(t,e);break}case E.TR:{t.tmplInsertionModeStack[0]=$.IN_TABLE_BODY,t.insertionMode=$.IN_TABLE_BODY,hp(t,e);break}case E.TD:case E.TH:{t.tmplInsertionModeStack[0]=$.IN_ROW,t.insertionMode=$.IN_ROW,pp(t,e);break}default:t.tmplInsertionModeStack[0]=$.IN_BODY,t.insertionMode=$.IN_BODY,cr(t,e)}}function wV(t,e){e.tagID===E.TEMPLATE&&ca(t,e)}function n2(t,e){t.openElements.tmplCount>0?(t.openElements.popUntilTagNamePopped(E.TEMPLATE),t.activeFormattingElements.clearToLastMarker(),t.tmplInsertionModeStack.shift(),t._resetInsertionMode(),t.onEof(e)):d1(t,e)}function TV(t,e){e.tagID===E.HTML?cr(t,e):gh(t,e)}function r2(t,e){var n;if(e.tagID===E.HTML){if(t.fragmentContext||(t.insertionMode=$.AFTER_AFTER_BODY),t.options.sourceCodeLocationInfo&&t.openElements.tagIDs[0]===E.HTML){t._setEndLocation(t.openElements.items[0],e);const r=t.openElements.items[1];r&&!(!((n=t.treeAdapter.getNodeSourceCodeLocation(r))===null||n===void 0)&&n.endTag)&&t._setEndLocation(r,e)}}else gh(t,e)}function gh(t,e){t.insertionMode=$.IN_BODY,dp(t,e)}function SV(t,e){switch(e.tagID){case E.HTML:{cr(t,e);break}case E.FRAMESET:{t._insertElement(e,be.HTML);break}case E.FRAME:{t._appendElement(e,be.HTML),e.ackSelfClosing=!0;break}case E.NOFRAMES:{Ni(t,e);break}}}function _V(t,e){e.tagID===E.FRAMESET&&!t.openElements.isRootHtmlElementCurrent()&&(t.openElements.pop(),!t.fragmentContext&&t.openElements.currentTagId!==E.FRAMESET&&(t.insertionMode=$.AFTER_FRAMESET))}function CV(t,e){switch(e.tagID){case E.HTML:{cr(t,e);break}case E.NOFRAMES:{Ni(t,e);break}}}function AV(t,e){e.tagID===E.HTML&&(t.insertionMode=$.AFTER_AFTER_FRAMESET)}function kV(t,e){e.tagID===E.HTML?cr(t,e):Hf(t,e)}function Hf(t,e){t.insertionMode=$.IN_BODY,dp(t,e)}function NV(t,e){switch(e.tagID){case E.HTML:{cr(t,e);break}case E.NOFRAMES:{Ni(t,e);break}}}function RV(t,e){e.chars=cn,t._insertCharacters(e)}function IV(t,e){t._insertCharacters(e),t.framesetOk=!1}function i2(t){for(;t.treeAdapter.getNamespaceURI(t.openElements.current)!==be.HTML&&t.openElements.currentTagId!==void 0&&!t._isIntegrationPoint(t.openElements.currentTagId,t.openElements.current);)t.openElements.pop()}function OV(t,e){if(K$(e))i2(t),t._startTagOutsideForeignContent(e);else{const n=t._getAdjustedCurrentElement(),r=t.treeAdapter.getNamespaceURI(n);r===be.MATHML?zN(e):r===be.SVG&&(Y$(e),jN(e)),u1(e),e.selfClosing?t._appendElement(e,r):t._insertElement(e,r),e.ackSelfClosing=!0}}function MV(t,e){if(e.tagID===E.P||e.tagID===E.BR){i2(t),t._endTagOutsideForeignContent(e);return}for(let n=t.openElements.stackTop;n>0;n--){const r=t.openElements.items[n];if(t.treeAdapter.getNamespaceURI(r)===be.HTML){t._endTagOutsideForeignContent(e);break}const i=t.treeAdapter.getTagName(r);if(i.toLowerCase()===e.tagName){e.tagName=i,t.openElements.shortenToLength(n);break}}}re.AREA,re.BASE,re.BASEFONT,re.BGSOUND,re.BR,re.COL,re.EMBED,re.FRAME,re.HR,re.IMG,re.INPUT,re.KEYGEN,re.LINK,re.META,re.PARAM,re.SOURCE,re.TRACK,re.WBR;const DV=/<(\/?)(iframe|noembed|noframes|plaintext|script|style|textarea|title|xmp)(?=[\t\n\f\r />])/gi,LV=new Set(["mdxFlowExpression","mdxJsxFlowElement","mdxJsxTextElement","mdxTextExpression","mdxjsEsm"]),OT={sourceCodeLocationInfo:!0,scriptingEnabled:!1};function s2(t,e){const n=VV(t),r=Ok("type",{handlers:{root:PV,element:FV,text:BV,comment:a2,doctype:UV,raw:zV},unknown:jV}),i={parser:n?new NT(OT):NT.getFragmentParser(void 0,OT),handle(l){r(l,i)},stitches:!1,options:e||{}};r(t,i),Bl(i,Ji());const s=n?i.parser.document:i.parser.getFragment(),o=Pj(s,{file:i.options.file});return i.stitches&&Pc(o,"comment",function(l,c,d){const f=l;if(f.value.stitch&&d&&c!==void 0){const p=d.children;return p[c]=f.value.stitch,c}}),o.type==="root"&&o.children.length===1&&o.children[0].type===t.type?o.children[0]:o}function o2(t,e){let n=-1;if(t)for(;++n4&&(e.parser.tokenizer.state=0);const n={type:yt.CHARACTER,chars:t.value,location:Hc(t)};Bl(e,Ji(t)),e.parser.currentToken=n,e.parser._processToken(e.parser.currentToken)}function UV(t,e){const n={type:yt.DOCTYPE,name:"html",forceQuirks:!1,publicId:"",systemId:"",location:Hc(t)};Bl(e,Ji(t)),e.parser.currentToken=n,e.parser._processToken(e.parser.currentToken)}function HV(t,e){e.stitches=!0;const n=GV(t);if("children"in t&&"children"in n){const r=s2({type:"root",children:t.children},e.options);n.children=r.children}a2({type:"comment",value:{stitch:n}},e)}function a2(t,e){const n=t.value,r={type:yt.COMMENT,data:n,location:Hc(t)};Bl(e,Ji(t)),e.parser.currentToken=r,e.parser._processToken(e.parser.currentToken)}function zV(t,e){if(e.parser.tokenizer.preprocessor.html="",e.parser.tokenizer.preprocessor.pos=-1,e.parser.tokenizer.preprocessor.lastGapPos=-2,e.parser.tokenizer.preprocessor.gapStack=[],e.parser.tokenizer.preprocessor.skipNextNewLine=!1,e.parser.tokenizer.preprocessor.lastChunkWritten=!1,e.parser.tokenizer.preprocessor.endOfChunkHit=!1,e.parser.tokenizer.preprocessor.isEol=!1,l2(e,Ji(t)),e.parser.tokenizer.write(e.options.tagfilter?t.value.replace(DV,"<$1$2"):t.value,!1),e.parser.tokenizer._runParsingLoop(),e.parser.tokenizer.state===72||e.parser.tokenizer.state===78){e.parser.tokenizer.preprocessor.lastChunkWritten=!0;const n=e.parser.tokenizer._consume();e.parser.tokenizer._callState(n)}}function jV(t,e){const n=t;if(e.options.passThrough&&e.options.passThrough.includes(n.type))HV(n,e);else{let r="";throw LV.has(n.type)&&(r=". It looks like you are using MDX nodes with `hast-util-raw` (or `rehype-raw`). If you use this because you are using remark or rehype plugins that inject `'html'` nodes, then please raise an issue with that plugin, as its a bad and slow idea. If you use this because you are using markdown syntax, then you have to configure this utility (or plugin) to pass through these nodes (see `passThrough` in docs), but you can also migrate to use the MDX syntax"),new Error("Cannot compile `"+n.type+"` node"+r)}}function Bl(t,e){l2(t,e);const n=t.parser.tokenizer.currentCharacterToken;n&&n.location&&(n.location.endLine=t.parser.tokenizer.preprocessor.line,n.location.endCol=t.parser.tokenizer.preprocessor.col+1,n.location.endOffset=t.parser.tokenizer.preprocessor.offset+1,t.parser.currentToken=n,t.parser._processToken(t.parser.currentToken)),t.parser.tokenizer.paused=!1,t.parser.tokenizer.inLoop=!1,t.parser.tokenizer.active=!1,t.parser.tokenizer.returnState=_n.DATA,t.parser.tokenizer.charRefCode=-1,t.parser.tokenizer.consumedAfterSnapshot=-1,t.parser.tokenizer.currentLocation=null,t.parser.tokenizer.currentCharacterToken=null,t.parser.tokenizer.currentToken=null,t.parser.tokenizer.currentAttr={name:"",value:""}}function l2(t,e){if(e&&e.offset!==void 0){const n={startLine:e.line,startCol:e.column,startOffset:e.offset,endLine:-1,endCol:-1,endOffset:-1};t.parser.tokenizer.preprocessor.lineStartPos=-e.column+1,t.parser.tokenizer.preprocessor.droppedBufferSize=e.offset,t.parser.tokenizer.preprocessor.line=e.line,t.parser.tokenizer.currentLocation=n}}function $V(t,e){const n=t.tagName.toLowerCase();if(e.parser.tokenizer.state===_n.PLAINTEXT)return;Bl(e,Ji(t));const r=e.parser.openElements.current;let i="namespaceURI"in r?r.namespaceURI:Ho.html;i===Ho.html&&n==="svg"&&(i=Ho.svg);const s=Qj({...t,children:[]},{space:i===Ho.svg?"svg":"html"}),o={type:yt.START_TAG,tagName:n,tagID:Fl(n),selfClosing:!1,ackSelfClosing:!1,attrs:"attrs"in s?s.attrs:[],location:Hc(t)};e.parser.currentToken=o,e.parser._processToken(e.parser.currentToken),e.parser.tokenizer.lastStartTagName=n}function WV(t,e){const n=t.tagName.toLowerCase();if(!e.parser.tokenizer.inForeignNode&&s$.includes(n)||e.parser.tokenizer.state===_n.PLAINTEXT)return;Bl(e,op(t));const r={type:yt.END_TAG,tagName:n,tagID:Fl(n),selfClosing:!1,ackSelfClosing:!1,attrs:[],location:Hc(t)};e.parser.currentToken=r,e.parser._processToken(e.parser.currentToken),n===e.parser.tokenizer.lastStartTagName&&(e.parser.tokenizer.state===_n.RCDATA||e.parser.tokenizer.state===_n.RAWTEXT||e.parser.tokenizer.state===_n.SCRIPT_DATA)&&(e.parser.tokenizer.state=_n.DATA)}function VV(t){const e=t.type==="root"?t.children[0]:t;return!!(e&&(e.type==="doctype"||e.type==="element"&&e.tagName.toLowerCase()==="html"))}function Hc(t){const e=Ji(t)||{line:void 0,column:void 0,offset:void 0},n=op(t)||{line:void 0,column:void 0,offset:void 0};return{startLine:e.line,startCol:e.column,startOffset:e.offset,endLine:n.line,endCol:n.column,endOffset:n.offset}}function GV(t){return"children"in t?El({...t,children:[]}):El(t)}function KV(t){return function(e,n){return s2(e,{...t,file:n})}}const YV="_markdown_n2mv1_5",qV={markdown:YV};function XV(t){return t?.replace?.(/\\n/g,` `)?.replace(/\n(?!-)/g,"
")||t}const u2=({children:t,className:e})=>w.jsx(TU,{components:{p:"div",img:({node:n,...r})=>w.jsx("img",{...r,loading:"lazy",alt:""})},rehypePlugins:[Sj,KV],remarkPlugins:[Pz],className:Xe("leading-[19px]",qV.markdown,e),children:XV(t)}),c2=({events:t,sessionId:e,existingFlagValue:n,onFlag:r})=>{const[i]=lt(ws),[s,o]=T.useState(n||""),l=t?.[0]?.trace_id,c=async()=>{await XD("Parlant-flags","message_flags",l,{sessionId:e,traceId:l,flagValue:s||"This message is flagged"},"update",{name:"sessionIndex",keyPath:"sessionId"}),r?.(s||""),i.closeDialog()},d=async()=>{await QD("Parlant-flags","message_flags",l),r?.(""),i.closeDialog()};return w.jsxs("div",{className:"px-[24px] pb-3 flex flex-col gap-3 h-full",children:[w.jsx("div",{children:w.jsx("p",{className:"text-[16px] text-[#959595]",children:"Feedback provided here will show up in the session's exported CSV file."})}),w.jsx("div",{className:"flex flex-col gap-1 mt-[26px] overflow-auto",children:t.map(f=>w.jsx("div",{className:"message-bubble [&>*]:w-full [&_*]:cursor-default",children:w.jsx("div",{className:"px-[22px] py-[20px] bg-[#F5F9F7] rounded-[22px] mb-[10px] !w-fit max-w-[90%]",children:f?.data?.message})}))}),w.jsx(ip,{autoFocus:!0,placeholder:"Enter your flag reason",value:s,onChange:f=>o(f.target.value),className:"!ring-0 !ring-offset-0 flex-1 !resize-none text-[16px] placeholder:text-[#959595]"}),w.jsxs("div",{className:"flex justify-end gap-3",children:[w.jsx(An,{variant:"outline",onClick:()=>i.closeDialog(),children:"Cancel"}),n&&w.jsx(An,{variant:"outline",onClick:d,children:"Unflag"}),w.jsx(An,{className:"bg-green-main hover:bg-[#005C3F]",onClick:c,children:"Save"})]})]})},QV=({draft:t="",open:e=!1})=>{const[n,r]=T.useState(!1);return T.useEffect(()=>{e&&r(!0)},[e]),w.jsxs("div",{className:Xe("group/main flex !origin-top min-w-full overflow-hidden",!e&&!n&&"h-0 opacity-0",e?"animate-slide-down":n?"animate-slide-up":""),children:[w.jsx("div",{className:"text-gray-400 relative px-[22px] peer/draft py-[20px] bg-[#F5F6F8] rounded-[22px] mb-[16px] max-w-[min(560px,calc(100%-30px))] min-w-[min(560px,100%)]",children:w.jsx(u2,{className:"leading-[26px]",children:t})}),w.jsx("div",{className:Xe("mx-[10px] self-stretch relative invisible items-center flex group-hover/main:visible peer-hover:visible hover:visible"),children:w.jsx(Xn,{value:"Copy",side:"top",children:w.jsx("div",{"data-testid":"copy-button",role:"button",onClick:()=>hl(t||""),className:"group cursor-pointer",children:w.jsx("img",{src:"icons/copy.svg",alt:"edit",className:"block opacity-50 rounded-[10px] group-hover:bg-[#EBECF0] size-[30px] p-[5px]"})})})})]})},ZV=({event:t,isFirstMessageInDate:e,showLogs:n,isContinual:r,showLogsForMessage:i,setIsEditing:s,flagged:o,flaggedChanged:l,sameTraceMessages:c})=>{const d=T.useRef(null),[f]=lt(oa),[p]=lt(np),m=T.useRef(null),[g,x]=T.useState(!1),[,v]=T.useState(1),[S]=lt(ws),[C]=lt(As),A=t?.data?.message||"",k=t?.data?.chunks,M=k!==void 0&&k.length>0&&k[k.length-1]===null,[F,I]=T.useState(M?A.length:0),[D,G]=T.useState(M?A.length:0),X=T.useRef(M?A.length:0),P=T.useRef(null);T.useEffect(()=>{if(!m?.current)return;const oe=Math.floor(m.current.offsetHeight/24);v(oe+1)},[m,g]),T.useEffect(()=>{if(A.length=A.length)return;const oe=30,pe=4,ue=setInterval(()=>{const J=X.current,he=A.length;if(J>=he){clearInterval(ue);return}const _e=Math.min(J+pe,he);X.current=_e,I(_e)},oe);return()=>clearInterval(ue)},[A]);const Y=T.useRef(0);Y.current=F,T.useEffect(()=>{if(FD&&!P.current&&(P.current=setTimeout(()=>{P.current=null,G(Y.current)},400))},[F,D]),T.useEffect(()=>()=>{P.current&&clearTimeout(P.current)},[]);const z=t.source==="customer"||t.source==="customer_ui",ie=t.serverStatus,Z=p?.id==="guest",ee=Z?"G":p?.name?.[0]?.toUpperCase(),ae=i&&i.id===t.id,de=U0((z?p?.id:f?.id)||"",z?"customer":"agent"),j=z?p?.name:f?.name,W=z&&Z?"Guest":j,O=c?.some(oe=>oe.serverStatus&&oe.serverStatus!=="ready"&&oe.serverStatus!=="error"),U=k!==void 0&&(k.length===0||k[k.length-1]!==null),Q=U||k!==void 0&&(F{const oe=d.current?.closest(".messages");if(!oe)return;const{scrollTop:pe,scrollHeight:ue,clientHeight:J}=oe,he=ue-pe-J<150;Q&&d.current&&he?d.current.scrollIntoView({behavior:"smooth",block:"end"}):R.current&&!Q&&he&&oe.scrollTo({top:oe.scrollHeight,behavior:"smooth"}),R.current=Q},[F,Q]),w.jsx(w.Fragment,{children:w.jsx("div",{className:Xe(z?"justify-end":"justify-start","flex-1 flex max-w-[min(1000px,100%)] items-end w-[calc(100%-412px)] max-[1440px]:w-[calc(100%-160px)] max-[900px]:w-[calc(100%-40px)]"),children:w.jsxs("div",{className:"relative max-w-[80%]",children:[(!r||e)&&w.jsxs("div",{className:On("flex items-center mb-[12px] mt-[46px] max-w-[min(560px,100%)]",z&&"justify-self-end",e&&"mt-[0]",z&&"flex-row-reverse"),children:[w.jsxs("div",{className:On("flex items-center contents",z&&"flex-row-reverse"),children:[w.jsx("div",{className:Xe("size-[26px] min-h-[26px] min-w-[26px] flex rounded-[6.5px] select-none items-center justify-center font-semibold",z?"ms-[8px]":"me-[8px]"),style:{color:z?"white":de.text,background:z?de.iconBackground:de?.background},children:(z?ee?.[0]:f?.name?.[0])?.toUpperCase()}),w.jsx("div",{className:"font-medium text-[14px] text-[#282828] truncate",children:W})]}),w.jsxs("div",{className:"flex items-center flex-1 justify-end",children:[!z&&c?.some(oe=>oe.data?.draft)&&w.jsx("div",{className:"flex items-center me-[6px] pe-[6px] border-e border-[#EBECF0]",children:w.jsx(Xn,{value:g?"Hide Draft":"Show Draft",side:"top",children:w.jsx(An,{"data-selected":g,variant:"ghost",className:"flex p-1 h-fit items-center gap-1",onClick:()=>x(!g),children:w.jsxs("div",{className:"text-[14px] text-[#777] font-normal px-[.25em] flex items-center gap-[6px]",children:[g?w.jsx(L4,{size:16,color:"#777"}):w.jsx(D4,{size:16,color:"#777"}),"Draft"]})})})}),o&&w.jsx("div",{className:"flex items-center gap-1 pe-[6px] me-[6px] border-e border-[#EBECF0]",children:w.jsx(Xn,{value:"View comment",side:"top",children:w.jsxs(An,{variant:"ghost",className:"flex p-1 h-fit items-center gap-1",onClick:()=>S.openDialog("Flag Response",w.jsx(c2,{existingFlagValue:o||"",events:c||[t],sessionId:C?.id,onFlag:l}),{width:"600px",height:"636px"}),children:[w.jsx(nA,{size:16,color:"#777"}),w.jsx("div",{className:"text-[14px] text-[#777] font-normal px-[.25em]",children:"Flagged"})]})})}),!z&&w.jsx(Xn,{value:"View message actions and logs",side:"top",children:w.jsxs(An,{"data-selected":ae,variant:"ghost",className:"flex p-1 h-fit items-center gap-1",onClick:()=>n(t),children:[w.jsx(B4,{size:16,color:"#777"}),w.jsx("div",{className:"text-[14px] text-[#777] font-normal px-[.25em]",children:"Inspect"})]})})]})]}),w.jsx(QV,{open:g,draft:c?.find(oe=>oe.data?.draft)?.data?.draft||""}),w.jsx("div",{className:"group/main relative",children:w.jsxs("div",{className:Xe("flex items-center max-w-full",z&&"flex-row-reverse"),children:[w.jsx("div",{className:"max-w-full",children:w.jsxs("div",{ref:d,tabIndex:0,"data-testid":"message",className:Xe("bg-green-light border-[2px] hover:bg-[#F5F9F3] text-black border-transparent",z&&ie==="error"&&"!bg-[#FDF2F1] hover:!bg-[#F5EFEF]","max-w-[min(560px,100%)] peer w-[560px] flex items-center relative",t?.serverStatus==="pending"&&"opacity-50","p-[20px_22px_24px_22px] rounded-[22px]"),children:[w.jsx("div",{className:Xe("markdown overflow-hidden relative min-w-[200px] max-w-[608px] [word-break:break-word] font-light text-[16px] pe-[38px]"),children:w.jsxs("span",{ref:m,children:[Q?w.jsxs(w.Fragment,{children:[w.jsx("span",{className:On("leading-[26px]"),children:A.slice(0,D)}),F>D&&w.jsx("span",{className:On("leading-[26px]","animate-fade-in-fast"),children:A.slice(D,F)},D)]}):w.jsx(u2,{className:On("leading-[26px]"),children:A}),U&&w.jsx("span",{className:"inline-block w-[2px] h-[1em] bg-current align-text-bottom ml-[1px] animate-pulse"})]})}),w.jsx("div",{className:Xe("flex h-full font-normal text-[11px] text-[#AEB4BB] pe-[20px] font-inter self-end items-end whitespace-nowrap leading-[14px]","")})]})}),w.jsxs("div",{className:Xe("mx-[10px] self-stretch relative invisible items-center flex group-hover/main:visible peer-hover:visible hover:visible"),children:[w.jsx(Xn,{value:"Copy",side:"top",children:w.jsx("div",{"data-testid":"copy-button",role:"button",onClick:()=>hl(t?.data?.message||""),className:"group cursor-pointer",children:w.jsx("img",{src:"icons/copy.svg",alt:"edit",className:"block opacity-50 rounded-[10px] group-hover:bg-[#EBECF0] size-[30px] p-[5px]"})})}),z&&!O&&w.jsx(Xn,{value:"Edit",side:"top",children:w.jsx("div",{"data-testid":"edit-button",role:"button",onClick:()=>s?.(!0),className:"group cursor-pointer",children:w.jsx("img",{src:"icons/edit-message.svg",alt:"edit",className:"block opacity-50 rounded-[10px] group-hover:bg-[#EBECF0] size-[30px] p-[5px]"})})})]})]})})]})})})},JV=({event:t,resendMessageFn:e,setIsEditing:n})=>{const r=T.useRef(null),i=T.useRef(null),[s,o]=T.useState(t?.data?.message||""),[l]=lt(As);return T.useEffect(()=>{i?.current?.select()},[i?.current]),T.useEffect(()=>{r?.current?.scrollIntoView({behavior:"smooth",block:"nearest"})},[r?.current]),w.jsxs("div",{ref:r,className:"w-full p-[16px] ps-[6px] pe-[6px] rounded-[16px] max-w-[min(560px,90%)] rounded-br-none border origin-bottom bg-[#f5f6f8] ",style:{transformOrigin:"bottom"},children:[w.jsx(ip,{ref:i,className:"[direction:ltr] resize-none h-[120px] pe-[108px] !ring-0 !ring-offset-0 border-none ps-[22px] bg-[#f5f6f8]",onChange:c=>o(c.target.value),defaultValue:s}),w.jsxs("div",{className:"pt-[10px] flex justify-end gap-[10px] pe-[12px] [direction:ltr]",children:[w.jsx(An,{variant:"ghost",onClick:()=>n?.(!1),className:"rounded-[10px] hover:bg-white",children:"Cancel"}),w.jsx(An,{disabled:!s?.trim()||s?.trim()===t?.data?.message,className:"rounded-[10px]",onClick:()=>{e?.(l?.id||"",s?.trim()),n?.(!1)},children:"Apply"})]})]})};function eG({event:t,isFirstMessageInDate:e,isContinual:n,showLogs:r,showLogsForMessage:i,resendMessageFn:s,flagged:o,flaggedChanged:l,sameTraceMessages:c}){const[d,f]=T.useState(!1);return w.jsx("div",{className:Xe(d&&"[direction:rtl] flex justify-center"),children:w.jsxs("div",{className:Xe("flex py-[3px] mx-0 mb-1 w-full justify-between animate-fade-in scrollbar",d&&"flex-1 flex justify-start max-w-[1000px] items-end w-[calc(100%-412px)] max-[2100px]:w-[calc(100%-200px)] self-end max-[1700px]:w-[calc(100%-40px)]"),children:[w.jsx(Qa,{}),d?w.jsx(JV,{resendMessageFn:s,setIsEditing:f,event:t,isContinual:n,showLogs:r,showLogsForMessage:i}):w.jsx(ZV,{isFirstMessageInDate:e,setIsEditing:f,event:t,isContinual:n,showLogs:r,showLogsForMessage:i,flagged:o,flaggedChanged:l,sameTraceMessages:c}),w.jsx(Qa,{})]})})}const tG=()=>{const[t]=lt(ws);return{openQuestionDialog:T.useCallback((n,r,i)=>{const s=()=>w.jsxs("div",{className:"h-full flex flex-col justify-between ms-[30px] me-[20px]",children:[w.jsx("p",{className:"mt-[10px]",children:r}),w.jsxs("div",{className:"h-[80px] flex items-center justify-end",children:[w.jsx(An,{"data-testid":"cancel",onClick:t.closeDialog,className:"hover:bg-[#EBE9F5] bg-[#F2F0FC] h-[46px] w-[96px] text-black rounded-[6px] py-[12px] px-[24px] me-[10px] text-[16px] font-normal",children:"Cancel"}),i.map(o=>o.isMainAction?w.jsx(An,{onClick:o.onClick,className:"h-[46px] w-[161px] bg-green-main hover:bg-[#005C3F] rounded-[6px] py-[10px] px-[29.5px] text-[15px] font-medium",children:o.text},o.text):w.jsx(An,{onClick:o.onClick,className:"hover:bg-[#EBE9F5] bg-[#F2F0FC] h-[46px] w-[96px] text-black rounded-[6px] py-[12px] px-[24px] me-[10px] text-[16px] font-normal",children:o.text},o.text))]})]});return t.openDialog(n,w.jsx(s,{}),{height:"230px",width:"480px"})},[t]),closeQuestionDialog:t.closeDialog}},MT=["CRITICAL","ERROR","WARNING","INFO","DEBUG","TRACE"],nG="Parlant",yi="logs",s0=2e3,DT=600*1e3;function mp(t=yi){return new Promise((e,n)=>{const r=indexedDB.open(nG,1);r.onupgradeneeded=()=>{const i=r.result;i.objectStoreNames.contains(t)||i.createObjectStore(t,{autoIncrement:!0}).createIndex("timestampIndex","timestamp",{unique:!1})},r.onsuccess=()=>e(r.result),r.onerror=()=>n(r.error)})}async function rG(t){const e=await mp();return new Promise((n,r)=>{const o=e.transaction(yi,"readonly").objectStore(yi).get(t);o.onsuccess=()=>n(o.result?.values||[]),o.onerror=()=>r(o.error)})}const iG=async t=>{if(YD())return;const r=(await mp()).transaction(yi,"readwrite").objectStore(yi),i=r.get(t.trace_id);i.onsuccess=()=>{const s=i.result,o=Date.now();s?.values?(s.values.push(t),r.put({timestamp:o,values:s.values},t.trace_id)):(!t.message?.trim().startsWith("HTTP")||t.message?.includes("/events"))&&r.put({timestamp:o,values:[t]},t.trace_id),window.dispatchEvent(new CustomEvent("new-log",{detail:{trace_id:t.trace_id}}))},i.onerror=()=>console.error(i.error)},cb=async t=>rG(t),sG=async(t,e)=>{const n=await cb(t),r=e?.content?.map(c=>c.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1")),i=r?.map(c=>`\\[?${c}\\]?`).join(".*?"),s=e.level?MT.indexOf(e.level):null,o=e.level?new Set(MT.filter((c,d)=>d<=s)):null,l=e.types?.length?new Set(e.types):null;return n.filter(c=>{if(o&&!o.has(c.level)||i&&!r?.every(f=>new RegExp(`\\[?${f}\\]?`,"i").test(`[${c.level}]${c.message}`)))return!1;if(l){const d=[...c.message.matchAll(/\[([^\]]+)\]/g)].map(m=>m?.[1]),p=(d[0]?.startsWith("T+")?d[1]:d[0])||"General";return l.has(p)}return!0})};async function oG(){const t=await mp();return new Promise((e,n)=>{try{const o=t.transaction(yi,"readonly").objectStore(yi).index("timestampIndex").openCursor(),l=[];o.onsuccess=c=>{const d=c.target.result;d?(d.primaryKey?.includes("::")&&l.push(d.value),d.continue()):e(l)},o.onerror=()=>n(o.error)}catch(r){t.close(),n(r)}})}async function aG(t=0){if(!t||t<=0){console.log("No valid deletion timestamp provided, skipping cleanup");return}try{const e=await mp(),n=e.transaction(yi,"readonly"),r=n.objectStore(yi),i=r.getAllKeys(),s=r.getAll();return new Promise((o,l)=>{let c=[],d=[];i.onsuccess=()=>{c=i.result,d.length>0&&f()},s.onsuccess=()=>{d=s.result,c.length>0&&f()};const f=()=>{const p=[];for(const S in c)d[S].timestamp{const C=g.delete(S);C.onsuccess=()=>{x++,x+v===p.length&&v>0&&console.warn(`Completed with ${v} errors`)},C.onerror=A=>{v++,console.error(`Failed to delete key ${S}:`,A.target.error)}}),m.oncomplete=()=>{e.close(),console.log(`Successfully deleted ${x} records older than ${new Date(t).toISOString()}`),o()},m.onerror=S=>{e.close(),l(S.target.error)}};n.onerror=p=>{e.close(),l(p.target.error)}})}catch(e){throw console.error("Error in deleteOldestLogs:",e),e}}async function LT(){try{const t=await oG();if(t[s0]){const e=t[t.length-s0]?.timestamp||0;console.log(`Log count exceeds maximum (${s0}), deleting logs before ${new Date(e)?.toLocaleString()}`),await aG(e),console.log("Cleanup completed")}}catch(t){console.error("Error during log cleanup:",t)}}let PT=null;function lG(){LT(),PT||(PT=window.setInterval(LT,DT),console.log(`Log cleanup scheduled every ${DT/1e3/60} minutes`))}lG();const uG=30;function cG(t,e){const[n,r]=T.useState(()=>{try{const s=globalThis.localStorage?.getItem(t);return s?JSON.parse(s):e}catch(s){return console.error("Error reading from localStorage",s),e}}),i=()=>{try{Array.isArray(n)&&n?.length>uG&&n.shift(),localStorage.setItem(t,JSON.stringify(n))}catch(s){if(console.error("Error writing to localStorage",s),s instanceof DOMException&&s.name==="QuotaExceededError"){const o=JSON.parse(localStorage.logs||"{}");Object.keys(o)[0]&&(console.log("deleting first log"),delete o[Object.keys(o)[0]],localStorage.setItem("logs",JSON.stringify(o)),i())}}};return T.useEffect(()=>{i()},[t,n]),[n,r]}function d2(t){const e=T.useRef({value:t,previous:t});return T.useMemo(()=>(e.current.value!==t&&(e.current.previous=e.current.value,e.current.value=t),e.current.previous),[t])}var gp="Checkbox",[dG,GX]=Cs(gp),[fG,h1]=dG(gp);function hG(t){const{__scopeCheckbox:e,checked:n,children:r,defaultChecked:i,disabled:s,form:o,name:l,onCheckedChange:c,required:d,value:f="on",internal_do_not_use_render:p}=t,[m,g]=Yo({prop:n,defaultProp:i??!1,onChange:c,caller:gp}),[x,v]=T.useState(null),[S,C]=T.useState(null),A=T.useRef(!1),k=x?!!o||!!x.closest("form"):!0,M={checked:m,disabled:s,setChecked:g,control:x,setControl:v,name:l,form:o,value:f,hasConsumerStoppedPropagationRef:A,required:d,defaultChecked:oo(i)?!1:i,isFormControl:k,bubbleInput:S,setBubbleInput:C};return w.jsx(fG,{scope:e,...M,children:pG(p)?p(M):r})}var f2="CheckboxTrigger",h2=T.forwardRef(({__scopeCheckbox:t,onKeyDown:e,onClick:n,...r},i)=>{const{control:s,value:o,disabled:l,checked:c,required:d,setControl:f,setChecked:p,hasConsumerStoppedPropagationRef:m,isFormControl:g,bubbleInput:x}=h1(f2,t),v=Pt(i,f),S=T.useRef(c);return T.useEffect(()=>{const C=s?.form;if(C){const A=()=>p(S.current);return C.addEventListener("reset",A),()=>C.removeEventListener("reset",A)}},[s,p]),w.jsx(xt.button,{type:"button",role:"checkbox","aria-checked":oo(c)?"mixed":c,"aria-required":d,"data-state":E2(c),"data-disabled":l?"":void 0,disabled:l,value:o,...r,ref:v,onKeyDown:je(e,C=>{C.key==="Enter"&&C.preventDefault()}),onClick:je(n,C=>{p(A=>oo(A)?!0:!A),x&&g&&(m.current=C.isPropagationStopped(),m.current||C.stopPropagation())})})});h2.displayName=f2;var p1=T.forwardRef((t,e)=>{const{__scopeCheckbox:n,name:r,checked:i,defaultChecked:s,required:o,disabled:l,value:c,onCheckedChange:d,form:f,...p}=t;return w.jsx(hG,{__scopeCheckbox:n,checked:i,defaultChecked:s,disabled:l,required:o,onCheckedChange:d,name:r,form:f,value:c,internal_do_not_use_render:({isFormControl:m})=>w.jsxs(w.Fragment,{children:[w.jsx(h2,{...p,ref:e,__scopeCheckbox:n}),m&&w.jsx(b2,{__scopeCheckbox:n})]})})});p1.displayName=gp;var p2="CheckboxIndicator",m2=T.forwardRef((t,e)=>{const{__scopeCheckbox:n,forceMount:r,...i}=t,s=h1(p2,n);return w.jsx(Zi,{present:r||oo(s.checked)||s.checked===!0,children:w.jsx(xt.span,{"data-state":E2(s.checked),"data-disabled":s.disabled?"":void 0,...i,ref:e,style:{pointerEvents:"none",...t.style}})})});m2.displayName=p2;var g2="CheckboxBubbleInput",b2=T.forwardRef(({__scopeCheckbox:t,...e},n)=>{const{control:r,hasConsumerStoppedPropagationRef:i,checked:s,defaultChecked:o,required:l,disabled:c,name:d,value:f,form:p,bubbleInput:m,setBubbleInput:g}=h1(g2,t),x=Pt(n,g),v=d2(s),S=S_(r);T.useEffect(()=>{const A=m;if(!A)return;const k=window.HTMLInputElement.prototype,F=Object.getOwnPropertyDescriptor(k,"checked").set,I=!i.current;if(v!==s&&F){const D=new Event("click",{bubbles:I});A.indeterminate=oo(s),F.call(A,oo(s)?!1:s),A.dispatchEvent(D)}},[m,v,s,i]);const C=T.useRef(oo(s)?!1:s);return w.jsx(xt.input,{type:"checkbox","aria-hidden":!0,defaultChecked:o??C.current,required:l,disabled:c,name:d,value:f,form:p,...e,tabIndex:-1,ref:x,style:{...e.style,...S,position:"absolute",pointerEvents:"none",opacity:0,margin:0,transform:"translateX(-100%)"}})});b2.displayName=g2;function pG(t){return typeof t=="function"}function oo(t){return t==="indeterminate"}function E2(t){return oo(t)?"indeterminate":t?"checked":"unchecked"}const y2=T.forwardRef(({className:t,...e},n)=>w.jsx(p1,{ref:n,className:Rt("peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",t),...e,children:w.jsx(m2,{className:Rt("flex items-center justify-center text-current"),children:w.jsx(CE,{className:"h-4 w-4",color:"black"})})}));y2.displayName=p1.displayName;function FT(t,[e,n]){return Math.min(n,Math.max(e,t))}var mG=[" ","Enter","ArrowUp","ArrowDown"],gG=[" ","Enter"],Qo="Select",[bp,Ep,bG]=mE(Qo),[Ul,KX]=Cs(Qo,[bG,Il]),yp=Il(),[EG,mo]=Ul(Qo),[yG,xG]=Ul(Qo),x2=t=>{const{__scopeSelect:e,children:n,open:r,defaultOpen:i,onOpenChange:s,value:o,defaultValue:l,onValueChange:c,dir:d,name:f,autoComplete:p,disabled:m,required:g,form:x}=t,v=yp(e),[S,C]=T.useState(null),[A,k]=T.useState(null),[M,F]=T.useState(!1),I=gE(d),[D,G]=Yo({prop:r,defaultProp:i??!1,onChange:s,caller:Qo}),[X,P]=Yo({prop:o,defaultProp:l,onChange:c,caller:Qo}),Y=T.useRef(null),z=S?x||!!S.closest("form"):!0,[ie,Z]=T.useState(new Set),ee=Array.from(ie).map(ae=>ae.props.value).join(";");return w.jsx(cE,{...v,children:w.jsxs(EG,{required:g,scope:e,trigger:S,onTriggerChange:C,valueNode:A,onValueNodeChange:k,valueNodeHasChildren:M,onValueNodeHasChildrenChange:F,contentId:Gi(),value:X,onValueChange:P,open:D,onOpenChange:G,dir:I,triggerPointerDownPosRef:Y,disabled:m,children:[w.jsx(bp.Provider,{scope:e,children:w.jsx(yG,{scope:t.__scopeSelect,onNativeOptionAdd:T.useCallback(ae=>{Z(de=>new Set(de).add(ae))},[]),onNativeOptionRemove:T.useCallback(ae=>{Z(de=>{const j=new Set(de);return j.delete(ae),j})},[]),children:n})}),z?w.jsxs(V2,{"aria-hidden":!0,required:g,tabIndex:-1,name:f,autoComplete:p,value:X,onChange:ae=>P(ae.target.value),disabled:m,form:x,children:[X===void 0?w.jsx("option",{value:""}):null,Array.from(ie)]},ee):null]})})};x2.displayName=Qo;var v2="SelectTrigger",w2=T.forwardRef((t,e)=>{const{__scopeSelect:n,disabled:r=!1,...i}=t,s=yp(n),o=mo(v2,n),l=o.disabled||r,c=Pt(e,o.onTriggerChange),d=Ep(n),f=T.useRef("touch"),[p,m,g]=K2(v=>{const S=d().filter(k=>!k.disabled),C=S.find(k=>k.value===o.value),A=Y2(S,v,C);A!==void 0&&o.onValueChange(A.value)}),x=v=>{l||(o.onOpenChange(!0),g()),v&&(o.triggerPointerDownPosRef.current={x:Math.round(v.pageX),y:Math.round(v.pageY)})};return w.jsx(dE,{asChild:!0,...s,children:w.jsx(xt.button,{type:"button",role:"combobox","aria-controls":o.contentId,"aria-expanded":o.open,"aria-required":o.required,"aria-autocomplete":"none",dir:o.dir,"data-state":o.open?"open":"closed",disabled:l,"data-disabled":l?"":void 0,"data-placeholder":G2(o.value)?"":void 0,...i,ref:c,onClick:je(i.onClick,v=>{v.currentTarget.focus(),f.current!=="mouse"&&x(v)}),onPointerDown:je(i.onPointerDown,v=>{f.current=v.pointerType;const S=v.target;S.hasPointerCapture(v.pointerId)&&S.releasePointerCapture(v.pointerId),v.button===0&&v.ctrlKey===!1&&v.pointerType==="mouse"&&(x(v),v.preventDefault())}),onKeyDown:je(i.onKeyDown,v=>{const S=p.current!=="";!(v.ctrlKey||v.altKey||v.metaKey)&&v.key.length===1&&m(v.key),!(S&&v.key===" ")&&mG.includes(v.key)&&(x(),v.preventDefault())})})})});w2.displayName=v2;var T2="SelectValue",S2=T.forwardRef((t,e)=>{const{__scopeSelect:n,className:r,style:i,children:s,placeholder:o="",...l}=t,c=mo(T2,n),{onValueNodeHasChildrenChange:d}=c,f=s!==void 0,p=Pt(e,c.onValueNodeChange);return lr(()=>{d(f)},[d,f]),w.jsx(xt.span,{...l,ref:p,style:{pointerEvents:"none"},children:G2(c.value)?w.jsx(w.Fragment,{children:o}):s})});S2.displayName=T2;var vG="SelectIcon",_2=T.forwardRef((t,e)=>{const{__scopeSelect:n,children:r,...i}=t;return w.jsx(xt.span,{"aria-hidden":!0,...i,ref:e,children:r||"▼"})});_2.displayName=vG;var wG="SelectPortal",C2=t=>w.jsx(Hh,{asChild:!0,...t});C2.displayName=wG;var Zo="SelectContent",A2=T.forwardRef((t,e)=>{const n=mo(Zo,t.__scopeSelect),[r,i]=T.useState();if(lr(()=>{i(new DocumentFragment)},[]),!n.open){const s=r;return s?Ac.createPortal(w.jsx(k2,{scope:t.__scopeSelect,children:w.jsx(bp.Slot,{scope:t.__scopeSelect,children:w.jsx("div",{children:t.children})})}),s):null}return w.jsx(N2,{...t,ref:e})});A2.displayName=Zo;var bi=10,[k2,go]=Ul(Zo),TG="SelectContentImpl",SG=Go("SelectContent.RemoveScroll"),N2=T.forwardRef((t,e)=>{const{__scopeSelect:n,position:r="item-aligned",onCloseAutoFocus:i,onEscapeKeyDown:s,onPointerDownOutside:o,side:l,sideOffset:c,align:d,alignOffset:f,arrowPadding:p,collisionBoundary:m,collisionPadding:g,sticky:x,hideWhenDetached:v,avoidCollisions:S,...C}=t,A=mo(Zo,n),[k,M]=T.useState(null),[F,I]=T.useState(null),D=Pt(e,J=>M(J)),[G,X]=T.useState(null),[P,Y]=T.useState(null),z=Ep(n),[ie,Z]=T.useState(!1),ee=T.useRef(!1);T.useEffect(()=>{if(k)return EE(k)},[k]),bE();const ae=T.useCallback(J=>{const[he,..._e]=z().map(ot=>ot.ref.current),[ke]=_e.slice(-1),Ve=document.activeElement;for(const ot of J)if(ot===Ve||(ot?.scrollIntoView({block:"nearest"}),ot===he&&F&&(F.scrollTop=0),ot===ke&&F&&(F.scrollTop=F.scrollHeight),ot?.focus(),document.activeElement!==Ve))return},[z,F]),de=T.useCallback(()=>ae([G,k]),[ae,G,k]);T.useEffect(()=>{ie&&de()},[ie,de]);const{onOpenChange:j,triggerPointerDownPosRef:W}=A;T.useEffect(()=>{if(k){let J={x:0,y:0};const he=ke=>{J={x:Math.abs(Math.round(ke.pageX)-(W.current?.x??0)),y:Math.abs(Math.round(ke.pageY)-(W.current?.y??0))}},_e=ke=>{J.x<=10&&J.y<=10?ke.preventDefault():k.contains(ke.target)||j(!1),document.removeEventListener("pointermove",he),W.current=null};return W.current!==null&&(document.addEventListener("pointermove",he),document.addEventListener("pointerup",_e,{capture:!0,once:!0})),()=>{document.removeEventListener("pointermove",he),document.removeEventListener("pointerup",_e,{capture:!0})}}},[k,j,W]),T.useEffect(()=>{const J=()=>j(!1);return window.addEventListener("blur",J),window.addEventListener("resize",J),()=>{window.removeEventListener("blur",J),window.removeEventListener("resize",J)}},[j]);const[O,U]=K2(J=>{const he=z().filter(Ve=>!Ve.disabled),_e=he.find(Ve=>Ve.ref.current===document.activeElement),ke=Y2(he,J,_e);ke&&setTimeout(()=>ke.ref.current.focus())}),Q=T.useCallback((J,he,_e)=>{const ke=!ee.current&&!_e;(A.value!==void 0&&A.value===he||ke)&&(X(J),ke&&(ee.current=!0))},[A.value]),R=T.useCallback(()=>k?.focus(),[k]),oe=T.useCallback((J,he,_e)=>{const ke=!ee.current&&!_e;(A.value!==void 0&&A.value===he||ke)&&Y(J)},[A.value]),pe=r==="popper"?db:R2,ue=pe===db?{side:l,sideOffset:c,align:d,alignOffset:f,arrowPadding:p,collisionBoundary:m,collisionPadding:g,sticky:x,hideWhenDetached:v,avoidCollisions:S}:{};return w.jsx(k2,{scope:n,content:k,viewport:F,onViewportChange:I,itemRefCallback:Q,selectedItem:G,onItemLeave:R,itemTextRefCallback:oe,focusSelectedItem:de,selectedItemText:P,position:r,isPositioned:ie,searchRef:O,children:w.jsx(Gh,{as:SG,allowPinchZoom:!0,children:w.jsx(Wh,{asChild:!0,trapped:A.open,onMountAutoFocus:J=>{J.preventDefault()},onUnmountAutoFocus:je(i,J=>{A.trigger?.focus({preventScroll:!0}),J.preventDefault()}),children:w.jsx(kc,{asChild:!0,disableOutsidePointerEvents:!0,onEscapeKeyDown:s,onPointerDownOutside:o,onFocusOutside:J=>J.preventDefault(),onDismiss:()=>A.onOpenChange(!1),children:w.jsx(pe,{role:"listbox",id:A.contentId,"data-state":A.open?"open":"closed",dir:A.dir,onContextMenu:J=>J.preventDefault(),...C,...ue,onPlaced:()=>Z(!0),ref:D,style:{display:"flex",flexDirection:"column",outline:"none",...C.style},onKeyDown:je(C.onKeyDown,J=>{const he=J.ctrlKey||J.altKey||J.metaKey;if(J.key==="Tab"&&J.preventDefault(),!he&&J.key.length===1&&U(J.key),["ArrowUp","ArrowDown","Home","End"].includes(J.key)){let ke=z().filter(Ve=>!Ve.disabled).map(Ve=>Ve.ref.current);if(["ArrowUp","End"].includes(J.key)&&(ke=ke.slice().reverse()),["ArrowUp","ArrowDown"].includes(J.key)){const Ve=J.target,ot=ke.indexOf(Ve);ke=ke.slice(ot+1)}setTimeout(()=>ae(ke)),J.preventDefault()}})})})})})})});N2.displayName=TG;var _G="SelectItemAlignedPosition",R2=T.forwardRef((t,e)=>{const{__scopeSelect:n,onPlaced:r,...i}=t,s=mo(Zo,n),o=go(Zo,n),[l,c]=T.useState(null),[d,f]=T.useState(null),p=Pt(e,D=>f(D)),m=Ep(n),g=T.useRef(!1),x=T.useRef(!0),{viewport:v,selectedItem:S,selectedItemText:C,focusSelectedItem:A}=o,k=T.useCallback(()=>{if(s.trigger&&s.valueNode&&l&&d&&v&&S&&C){const D=s.trigger.getBoundingClientRect(),G=d.getBoundingClientRect(),X=s.valueNode.getBoundingClientRect(),P=C.getBoundingClientRect();if(s.dir!=="rtl"){const Ve=P.left-G.left,ot=X.left-Ve,qe=D.left-ot,kt=D.width+qe,fn=Math.max(kt,G.width),nt=window.innerWidth-bi,Yt=FT(ot,[bi,Math.max(bi,nt-fn)]);l.style.minWidth=kt+"px",l.style.left=Yt+"px"}else{const Ve=G.right-P.right,ot=window.innerWidth-X.right-Ve,qe=window.innerWidth-D.right-ot,kt=D.width+qe,fn=Math.max(kt,G.width),nt=window.innerWidth-bi,Yt=FT(ot,[bi,Math.max(bi,nt-fn)]);l.style.minWidth=kt+"px",l.style.right=Yt+"px"}const Y=m(),z=window.innerHeight-bi*2,ie=v.scrollHeight,Z=window.getComputedStyle(d),ee=parseInt(Z.borderTopWidth,10),ae=parseInt(Z.paddingTop,10),de=parseInt(Z.borderBottomWidth,10),j=parseInt(Z.paddingBottom,10),W=ee+ae+ie+j+de,O=Math.min(S.offsetHeight*5,W),U=window.getComputedStyle(v),Q=parseInt(U.paddingTop,10),R=parseInt(U.paddingBottom,10),oe=D.top+D.height/2-bi,pe=z-oe,ue=S.offsetHeight/2,J=S.offsetTop+ue,he=ee+ae+J,_e=W-he;if(he<=oe){const Ve=Y.length>0&&S===Y[Y.length-1].ref.current;l.style.bottom="0px";const ot=d.clientHeight-v.offsetTop-v.offsetHeight,qe=Math.max(pe,ue+(Ve?R:0)+ot+de),kt=he+qe;l.style.height=kt+"px"}else{const Ve=Y.length>0&&S===Y[0].ref.current;l.style.top="0px";const qe=Math.max(oe,ee+v.offsetTop+(Ve?Q:0)+ue)+_e;l.style.height=qe+"px",v.scrollTop=he-oe+v.offsetTop}l.style.margin=`${bi}px 0`,l.style.minHeight=O+"px",l.style.maxHeight=z+"px",r?.(),requestAnimationFrame(()=>g.current=!0)}},[m,s.trigger,s.valueNode,l,d,v,S,C,s.dir,r]);lr(()=>k(),[k]);const[M,F]=T.useState();lr(()=>{d&&F(window.getComputedStyle(d).zIndex)},[d]);const I=T.useCallback(D=>{D&&x.current===!0&&(k(),A?.(),x.current=!1)},[k,A]);return w.jsx(AG,{scope:n,contentWrapper:l,shouldExpandOnScrollRef:g,onScrollButtonChange:I,children:w.jsx("div",{ref:c,style:{display:"flex",flexDirection:"column",position:"fixed",zIndex:M},children:w.jsx(xt.div,{...i,ref:p,style:{boxSizing:"border-box",maxHeight:"100%",...i.style}})})})});R2.displayName=_G;var CG="SelectPopperPosition",db=T.forwardRef((t,e)=>{const{__scopeSelect:n,align:r="start",collisionPadding:i=bi,...s}=t,o=yp(n);return w.jsx(fE,{...o,...s,ref:e,align:r,collisionPadding:i,style:{boxSizing:"border-box",...s.style,"--radix-select-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-select-content-available-width":"var(--radix-popper-available-width)","--radix-select-content-available-height":"var(--radix-popper-available-height)","--radix-select-trigger-width":"var(--radix-popper-anchor-width)","--radix-select-trigger-height":"var(--radix-popper-anchor-height)"}})});db.displayName=CG;var[AG,m1]=Ul(Zo,{}),fb="SelectViewport",I2=T.forwardRef((t,e)=>{const{__scopeSelect:n,nonce:r,...i}=t,s=go(fb,n),o=m1(fb,n),l=Pt(e,s.onViewportChange),c=T.useRef(0);return w.jsxs(w.Fragment,{children:[w.jsx("style",{dangerouslySetInnerHTML:{__html:"[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}"},nonce:r}),w.jsx(bp.Slot,{scope:n,children:w.jsx(xt.div,{"data-radix-select-viewport":"",role:"presentation",...i,ref:l,style:{position:"relative",flex:1,overflow:"hidden auto",...i.style},onScroll:je(i.onScroll,d=>{const f=d.currentTarget,{contentWrapper:p,shouldExpandOnScrollRef:m}=o;if(m?.current&&p){const g=Math.abs(c.current-f.scrollTop);if(g>0){const x=window.innerHeight-bi*2,v=parseFloat(p.style.minHeight),S=parseFloat(p.style.height),C=Math.max(v,S);if(C0?M:0,p.style.justifyContent="flex-end")}}}c.current=f.scrollTop})})})]})});I2.displayName=fb;var O2="SelectGroup",[kG,NG]=Ul(O2),M2=T.forwardRef((t,e)=>{const{__scopeSelect:n,...r}=t,i=Gi();return w.jsx(kG,{scope:n,id:i,children:w.jsx(xt.div,{role:"group","aria-labelledby":i,...r,ref:e})})});M2.displayName=O2;var D2="SelectLabel",L2=T.forwardRef((t,e)=>{const{__scopeSelect:n,...r}=t,i=NG(D2,n);return w.jsx(xt.div,{id:i.id,...r,ref:e})});L2.displayName=D2;var bh="SelectItem",[RG,P2]=Ul(bh),F2=T.forwardRef((t,e)=>{const{__scopeSelect:n,value:r,disabled:i=!1,textValue:s,...o}=t,l=mo(bh,n),c=go(bh,n),d=l.value===r,[f,p]=T.useState(s??""),[m,g]=T.useState(!1),x=Pt(e,A=>c.itemRefCallback?.(A,r,i)),v=Gi(),S=T.useRef("touch"),C=()=>{i||(l.onValueChange(r),l.onOpenChange(!1))};if(r==="")throw new Error("A must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder.");return w.jsx(RG,{scope:n,value:r,disabled:i,textId:v,isSelected:d,onItemTextChange:T.useCallback(A=>{p(k=>k||(A?.textContent??"").trim())},[]),children:w.jsx(bp.ItemSlot,{scope:n,value:r,disabled:i,textValue:f,children:w.jsx(xt.div,{role:"option","aria-labelledby":v,"data-highlighted":m?"":void 0,"aria-selected":d&&m,"data-state":d?"checked":"unchecked","aria-disabled":i||void 0,"data-disabled":i?"":void 0,tabIndex:i?void 0:-1,...o,ref:x,onFocus:je(o.onFocus,()=>g(!0)),onBlur:je(o.onBlur,()=>g(!1)),onClick:je(o.onClick,()=>{S.current!=="mouse"&&C()}),onPointerUp:je(o.onPointerUp,()=>{S.current==="mouse"&&C()}),onPointerDown:je(o.onPointerDown,A=>{S.current=A.pointerType}),onPointerMove:je(o.onPointerMove,A=>{S.current=A.pointerType,i?c.onItemLeave?.():S.current==="mouse"&&A.currentTarget.focus({preventScroll:!0})}),onPointerLeave:je(o.onPointerLeave,A=>{A.currentTarget===document.activeElement&&c.onItemLeave?.()}),onKeyDown:je(o.onKeyDown,A=>{c.searchRef?.current!==""&&A.key===" "||(gG.includes(A.key)&&C(),A.key===" "&&A.preventDefault())})})})})});F2.displayName=bh;var Hu="SelectItemText",B2=T.forwardRef((t,e)=>{const{__scopeSelect:n,className:r,style:i,...s}=t,o=mo(Hu,n),l=go(Hu,n),c=P2(Hu,n),d=xG(Hu,n),[f,p]=T.useState(null),m=Pt(e,C=>p(C),c.onItemTextChange,C=>l.itemTextRefCallback?.(C,c.value,c.disabled)),g=f?.textContent,x=T.useMemo(()=>w.jsx("option",{value:c.value,disabled:c.disabled,children:g},c.value),[c.disabled,c.value,g]),{onNativeOptionAdd:v,onNativeOptionRemove:S}=d;return lr(()=>(v(x),()=>S(x)),[v,S,x]),w.jsxs(w.Fragment,{children:[w.jsx(xt.span,{id:c.textId,...s,ref:m}),c.isSelected&&o.valueNode&&!o.valueNodeHasChildren?Ac.createPortal(s.children,o.valueNode):null]})});B2.displayName=Hu;var U2="SelectItemIndicator",H2=T.forwardRef((t,e)=>{const{__scopeSelect:n,...r}=t;return P2(U2,n).isSelected?w.jsx(xt.span,{"aria-hidden":!0,...r,ref:e}):null});H2.displayName=U2;var hb="SelectScrollUpButton",z2=T.forwardRef((t,e)=>{const n=go(hb,t.__scopeSelect),r=m1(hb,t.__scopeSelect),[i,s]=T.useState(!1),o=Pt(e,r.onScrollButtonChange);return lr(()=>{if(n.viewport&&n.isPositioned){let l=function(){const d=c.scrollTop>0;s(d)};const c=n.viewport;return l(),c.addEventListener("scroll",l),()=>c.removeEventListener("scroll",l)}},[n.viewport,n.isPositioned]),i?w.jsx($2,{...t,ref:o,onAutoScroll:()=>{const{viewport:l,selectedItem:c}=n;l&&c&&(l.scrollTop=l.scrollTop-c.offsetHeight)}}):null});z2.displayName=hb;var pb="SelectScrollDownButton",j2=T.forwardRef((t,e)=>{const n=go(pb,t.__scopeSelect),r=m1(pb,t.__scopeSelect),[i,s]=T.useState(!1),o=Pt(e,r.onScrollButtonChange);return lr(()=>{if(n.viewport&&n.isPositioned){let l=function(){const d=c.scrollHeight-c.clientHeight,f=Math.ceil(c.scrollTop)c.removeEventListener("scroll",l)}},[n.viewport,n.isPositioned]),i?w.jsx($2,{...t,ref:o,onAutoScroll:()=>{const{viewport:l,selectedItem:c}=n;l&&c&&(l.scrollTop=l.scrollTop+c.offsetHeight)}}):null});j2.displayName=pb;var $2=T.forwardRef((t,e)=>{const{__scopeSelect:n,onAutoScroll:r,...i}=t,s=go("SelectScrollButton",n),o=T.useRef(null),l=Ep(n),c=T.useCallback(()=>{o.current!==null&&(window.clearInterval(o.current),o.current=null)},[]);return T.useEffect(()=>()=>c(),[c]),lr(()=>{l().find(f=>f.ref.current===document.activeElement)?.ref.current?.scrollIntoView({block:"nearest"})},[l]),w.jsx(xt.div,{"aria-hidden":!0,...i,ref:e,style:{flexShrink:0,...i.style},onPointerDown:je(i.onPointerDown,()=>{o.current===null&&(o.current=window.setInterval(r,50))}),onPointerMove:je(i.onPointerMove,()=>{s.onItemLeave?.(),o.current===null&&(o.current=window.setInterval(r,50))}),onPointerLeave:je(i.onPointerLeave,()=>{c()})})}),IG="SelectSeparator",W2=T.forwardRef((t,e)=>{const{__scopeSelect:n,...r}=t;return w.jsx(xt.div,{"aria-hidden":!0,...r,ref:e})});W2.displayName=IG;var mb="SelectArrow",OG=T.forwardRef((t,e)=>{const{__scopeSelect:n,...r}=t,i=yp(n),s=mo(mb,n),o=go(mb,n);return s.open&&o.position==="popper"?w.jsx(hE,{...i,...r,ref:e}):null});OG.displayName=mb;var MG="SelectBubbleInput",V2=T.forwardRef(({__scopeSelect:t,value:e,...n},r)=>{const i=T.useRef(null),s=Pt(r,i),o=d2(e);return T.useEffect(()=>{const l=i.current;if(!l)return;const c=window.HTMLSelectElement.prototype,f=Object.getOwnPropertyDescriptor(c,"value").set;if(o!==e&&f){const p=new Event("change",{bubbles:!0});f.call(l,e),l.dispatchEvent(p)}},[o,e]),w.jsx(xt.select,{...n,style:{...D_,...n.style},ref:s,defaultValue:e})});V2.displayName=MG;function G2(t){return t===""||t===void 0}function K2(t){const e=Yi(t),n=T.useRef(""),r=T.useRef(0),i=T.useCallback(o=>{const l=n.current+o;e(l),(function c(d){n.current=d,window.clearTimeout(r.current),d!==""&&(r.current=window.setTimeout(()=>c(""),1e3))})(l)},[e]),s=T.useCallback(()=>{n.current="",window.clearTimeout(r.current)},[]);return T.useEffect(()=>()=>window.clearTimeout(r.current),[]),[n,i,s]}function Y2(t,e,n){const i=e.length>1&&Array.from(e).every(d=>d===e[0])?e[0]:e,s=n?t.indexOf(n):-1;let o=DG(t,Math.max(s,0));i.length===1&&(o=o.filter(d=>d!==n));const c=o.find(d=>d.textValue.toLowerCase().startsWith(i.toLowerCase()));return c!==n?c:void 0}function DG(t,e){return t.map((n,r)=>t[(e+r)%t.length])}var LG=x2,q2=w2,PG=S2,FG=_2,BG=C2,X2=A2,UG=I2,HG=M2,Q2=L2,Z2=F2,zG=B2,jG=H2,J2=z2,eR=j2,tR=W2;const $G=LG,WG=HG,VG=PG,nR=T.forwardRef(({className:t,children:e,...n},r)=>w.jsxs(q2,{ref:r,className:Rt("flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",t),...n,children:[e,w.jsx(FG,{asChild:!0,children:w.jsx(tA,{className:"h-4 w-4 opacity-50"})})]}));nR.displayName=q2.displayName;const rR=T.forwardRef(({className:t,...e},n)=>w.jsx(J2,{ref:n,className:Rt("flex cursor-default items-center justify-center py-1",t),...e,children:w.jsx(O4,{className:"h-4 w-4"})}));rR.displayName=J2.displayName;const iR=T.forwardRef(({className:t,...e},n)=>w.jsx(eR,{ref:n,className:Rt("flex cursor-default items-center justify-center py-1",t),...e,children:w.jsx(tA,{className:"h-4 w-4"})}));iR.displayName=eR.displayName;const sR=T.forwardRef(({className:t,children:e,position:n="popper",...r},i)=>w.jsx(BG,{children:w.jsxs(X2,{ref:i,className:Rt("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",n==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",t),position:n,...r,children:[w.jsx(rR,{}),w.jsx(UG,{className:Rt("p-1",n==="popper"&&"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"),children:e}),w.jsx(iR,{})]})}));sR.displayName=X2.displayName;const GG=T.forwardRef(({className:t,...e},n)=>w.jsx(Q2,{ref:n,className:Rt("py-1.5 pl-8 pr-2 text-sm font-semibold",t),...e}));GG.displayName=Q2.displayName;const oR=T.forwardRef(({className:t,children:e,...n},r)=>w.jsxs(Z2,{ref:r,className:Rt("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",t),...n,children:[w.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:w.jsx(jG,{children:w.jsx(CE,{className:"h-4 w-4"})})}),w.jsx(zG,{children:e})]}));oR.displayName=Z2.displayName;const KG=T.forwardRef(({className:t,...e},n)=>w.jsx(tR,{ref:n,className:Rt("-mx-1 my-1 h-px bg-muted",t),...e}));KG.displayName=tR.displayName;const BT=["GuidelineMatcher","ToolCaller","MessageEventComposer"],fs=["CRITICAL","ERROR","WARNING","INFO","DEBUG","TRACE"],Ef={GuidelineMatcher:{label:"Guideline Matcher",icon:"icons/filters/guideline-matcher-color.svg",color:"#419480"},MessageEventComposer:{label:"Message Event Composer",icon:"icons/filters/message-composer-color.svg",color:"#7E3A89"},ToolCaller:{label:"Tool Caller",icon:"icons/filters/tool-caller-color.svg",color:"#CB7714"}},YG=({className:t})=>w.jsx("div",{className:Xe("group cursor-pointer bg-white border-[#eeeeee] hover:bg-[#F3F5F9] hover:border-[#E4E6EA] border h-[30px] rounded-[6px] flex items-center w-full shadow-main",t),children:w.jsxs("div",{className:"flex items-center justify-center rounded-[3px] leading-[16px] h-[calc(100%-4px)] w-[calc(100%-4px)] py-[5px] px-[8px] pe-[6px]",children:[w.jsx("img",{src:"icons/text.svg",alt:"",className:"me-[5px]"}),w.jsx("p",{className:"text-nowrap font-normal text-[14px]",children:"Add Content Filter"})]})}),qG=({contentChanged:t,defaultValue:e})=>{const[n,r]=T.useState(e||""),i=()=>{n.trim()&&t(n)};return w.jsxs("div",{className:"px-[39px] py-[42px] flex flex-col gap-[22px]",children:[w.jsx("h2",{className:"text-[20px] font-normal",children:"Filter by content"}),w.jsx("div",{className:"border rounded-[5px] h-[38px] flex items-center bg-[#FBFBFB] hover:bg-[#F5F6F8] focus-within:!bg-white",children:w.jsx(sc,{value:n,onChange:s=>r(s.target.value),name:"filter",className:"h-[36px] !ring-0 !ring-offset-0 border-none text-[16px] bg-[#FBFBFB] hover:bg-[#F5F6F8] focus:!bg-white"})}),w.jsxs("div",{className:"buttons flex items-center gap-[16px] justify-end text-[16px] font-normal font-inter",children:[w.jsx(Gv,{className:"h-[38px] w-[84px] !bg-white text-[#656565] hover:text-[#151515] rounded-[5px] border",children:"Cancel"}),w.jsx(Gv,{onClick:i,className:"bg-green-main text-white h-[38px] w-[79px] hover:bg-green-hover rounded-[5px]",children:"Apply"})]})]})},UT=({contentChanged:t,content:e,children:n,className:r})=>w.jsxs(TA,{children:[w.jsx(iF,{className:"w-full",children:n||w.jsx(YG,{className:r})}),w.jsx(DE,{"aria-hidden":!1,children:w.jsxs(LE,{"aria-hidden":!1,className:"p-0 [&>button]:hidden z-[99]",children:[w.jsx(FE,{className:"hidden",children:"Filter by content"}),w.jsx(BE,{className:"hidden",children:"Filter by content"}),w.jsx(qG,{contentChanged:t,defaultValue:e||""})]})})]}),XG=({applyFn:t,def:e,filterId:n,className:r,showDropdown:i,showTags:s,deleteFilterTab:o})=>{const[l,c]=T.useState(structuredClone(e?.types||[])),[d,f]=T.useState(structuredClone(e?.content||[])),[p,m]=T.useState(e?.level||fs[fs.length-1]),[g,x]=T.useState();T.useEffect(()=>{if(s&&n&&n!==g){const A=structuredClone(e?.types||BT),k=e?.level||fs[fs.length-1],M=e?.content||[];c(A),m(k),f(M),t(A,k,M),x(n)}},[n]),T.useEffect(()=>{c(e?.types||[]),m(e?.level||fs[fs.length-1]),f(e?.content||[])},[e]);const v=({type:A,className:k})=>w.jsxs("div",{className:Xe("group border cursor-default border-[#EEEEEE] h-[30px] flex items-center gap-[8px] pt-[6px] pb-[5px] ps-[6px] rounded-[5px] pe-[6px] hover:bg-white",k),children:[w.jsx("img",{src:Ef[A].icon,alt:A}),w.jsx("p",{className:"text-nowrap font-normal text-[14px]",children:Ef[A].label})]},A),S=({text:A,index:k,apply:M,deleted:F,wrapperClassName:I,className:D,deleteButtonClassName:G})=>w.jsx(Xn,{value:A,side:"top",delayDuration:1e3,children:w.jsx("div",{className:Xe("group px-[2px] cursor-default max-w-[320px] bg-white border-[#EEEEEE] border h-[30px] rounded-[5px] flex justify-center items-center w-fit",I),children:w.jsxs("div",{className:Xe("flex items-center w-full justify-between max-w-full rounded-[3px] h-[calc(100%-4px)] py-[5px] ps-[5px] pe-[6px] gap-[8px]",D),children:[w.jsxs("div",{className:Xe("flex items-center gap-[8px] leading-[16px] max-w-[-webkit-fill-available]",F&&"max-w-[calc(100%-25px)]"),children:[w.jsx("img",{src:"icons/text.svg",alt:""}),w.jsx("p",{className:"text-nowrap cursor-default max-w-full overflow-hidden text-ellipsis font-light text-[14px]",children:A})]}),F&&w.jsx(gl,{role:"button",className:Xe("invisible min-w-[18px] size-[18px] group-hover:visible rounded-[3px]",G),onClick:X=>{X.stopPropagation();const P=d?.filter((Y,z)=>z!==k);M&&(f(P),t(l,p,P)),F?.()}})]})},A)}),C=()=>{const[A,k]=T.useState(!1),[M,F]=T.useState(structuredClone(e?.types||[])),[I,D]=T.useState(structuredClone(e?.content||[])),[G,X]=T.useState(e?.level||fs[fs.length-1]),P=T.useRef(null),[Y,z]=T.useState(!1),ie=(ee,ae)=>{F(de=>(ae?de.push(ee):de=de.filter(W=>W!==ee),[...new Set(de)]))};T.useEffect(()=>{A||(F(structuredClone(e?.types||[])),D(structuredClone(e?.content||[])))},[A]),T.useEffect(()=>{P?.current&&(Y4(P.current)<218?z(!0):z(!1))},[P?.current?.scrollWidth,A]);const Z=()=>{k(!A),F(structuredClone(e?.types||[])),D(structuredClone(e?.content||[]))};return w.jsxs("div",{className:"wrapper relative flex items-center h-[30px]",ref:P,children:[w.jsx("div",{children:w.jsxs("div",{onClick:Z,role:"button",className:Xe("flex group bg-white rounded-[6px] items-center gap-[6px] max-h-[30px] h-[30px] w-[73px] min-w-max pe-[8px]",A&&"bg-white border-transparent"),children:[w.jsx("img",{src:"icons/funnel.svg",className:"[stroke-width:2px] size-[16px]"}),w.jsx("p",{className:"text-[14px] group-hover:underline font-medium select-none",children:"Edit Filters"})]})}),w.jsxs("div",{className:Xe("hidden border rounded-[7px] absolute top-[38px] left-0 w-[246px] z-50 bg-white",A&&"block",Y?"right-0 left-[unset]":""),children:[w.jsxs("div",{className:"flex justify-between items-center",children:[w.jsx("div",{className:"flex items-center gap-[6px] h-[35px] px-[14px]",children:w.jsx("p",{className:"text-[14px] font-normal",children:"Filter"})}),w.jsx("div",{role:"button",onClick:Z,className:"flex h-[24px] w-[24px] items-center me-[2px] justify-center",children:w.jsx("img",{src:"icons/close.svg",alt:"close"})})]}),w.jsx("hr",{className:"bg-[#EBECF0]"}),w.jsxs("div",{className:"flex gap-[6px] items-center px-[14px]",children:[w.jsx("p",{className:"text-[14px] font-normal",children:"Level:"}),w.jsxs($G,{value:G,onValueChange:ee=>X(ee),children:[w.jsx(nR,{className:"!ring-0 !ring-offset-0 h-[30px] m-auto my-[5px] capitalize border",children:w.jsx(VG,{placeholder:G?.toLowerCase()})}),w.jsx(sR,{className:"z-[999999]",children:w.jsx(WG,{children:fs.toReversed().map(ee=>w.jsx(oR,{value:ee,className:"capitalize",children:ee?.toLowerCase()},ee))})})]})]}),w.jsx("hr",{className:"bg-[#EBECF0]"}),w.jsx("div",{className:"flex flex-col gap-[4px] mt-[9px] pb-[11px] px-[8px]",children:BT.map(ee=>w.jsxs("div",{className:Xe("flex items-center rounded-[3px] h-[24px] py-[4px] ps-[4px] space-x-2 hover:bg-main",M.includes(ee)&&"!bg-gray-4"),children:[w.jsx(y2,{id:ee,checked:M?.includes(ee),className:"[&_svg]:[stroke:#006E53] border-black rounded-[2px] !bg-white",onCheckedChange:ae=>ie(ee,!!ae)}),w.jsxs("label",{className:"text-[14px] font-light w-full cursor-pointer flex gap-[8px] !ms-[12px]",htmlFor:ee,children:[w.jsx("img",{src:Ef[ee].icon,alt:ee}),Ef[ee].label]})]},ee))}),w.jsx("hr",{className:"bg-[#EBECF0]"}),w.jsx("div",{className:Xe("inputs flex flex-wrap gap-[6px] max-h-[200px] overflow-auto px-[14px] pb-[14px] pt-[11px]",!I?.length&&"h-0 p-0"),children:I?.map((ee,ae)=>w.jsx(UT,{content:ee,contentChanged:de=>{D(j=>(j[ae]=de,[...j]))},children:w.jsx(S,{text:ee,index:ae,apply:!1,deleted:()=>D(I.filter((de,j)=>j!==ae)),wrapperClassName:"w-full !border-0 bg-[#F5F6F8] hover:bg-[#EBECF0]",className:"justify-between !border-0 bg-[#F5F6F8] group-hover:bg-[#EBECF0]",deleteButtonClassName:"visible"})},ee))}),!!I?.length&&w.jsx("hr",{className:"bg-[#EBECF0] w-full"}),w.jsx("div",{className:"px-[14px] h-[54px] flex items-center",children:w.jsx(UT,{contentChanged:ee=>D(ae=>[...ae,ee])})}),w.jsx("hr",{className:"bg-[#EBECF0]"}),w.jsxs("div",{className:"buttons flex gap-[8px] items-center h-[47px] p-[6px]",children:[w.jsx(An,{onClick:()=>t([],"DEBUG",[]),variant:"ghost",className:"flex-1 text-[12px] bg-[#FAFAFA] hover:text-[#151515] hover:bg-[#F3F5F9] font-normal text-[#656565] h-[35px] w-[95px]",children:"Clear all"}),w.jsx(An,{variant:"ghost",onClick:()=>{t(M,G,I),k(!1)},className:"flex-1 ps-[12px] pe-[10px] text-[12px] font-normal !text-white bg-green-main hover:bg-[#005C3F] w-fit max-w-fit h-[35px]",children:"Apply"})]})]})]})};return w.jsxs("div",{className:"flex items-center justify-between pe-[14px] z-[1] bg-white",children:[w.jsx("div",{className:Xe("flex z-[1] pt-[10px] pb-[8px] pe-[12px] ps-[14px] gap-[8px] h-fit min-h-[58px]",(!!e?.types?.length||!!e?.content?.length)&&"min-h-[50px]",r),children:w.jsxs("div",{className:"filters-button flex items-start gap-[10px] flex-wrap",children:[s&&!!e?.types?.length&&e.types.map(A=>w.jsx(v,{type:A},A)),s&&e?.content?.map((A,k)=>w.jsx(S,{text:A,index:k,wrapperClassName:"cursor-auto"},A)),i&&w.jsx(C,{})]})}),o&&w.jsx(An,{onClick:()=>o(n),variant:"outline",className:"self-start mt-[10px] min-h-[28px] min-w-[28px] size-[28px] p-0 border border-[#EEEEEE] rounded-[6px] shadow-main",children:w.jsx(gl,{className:"size-[14px] min-h-[14px] min-w-[14px]"})})]})},HT=T.memo(XG),o0=({title:t,subTitle:e,wrapperClassName:n,className:r,imgClassName:i,imgUrl:s})=>w.jsx("div",{className:Xe("flex flex-col m-auto justify-center items-center w-full h-full",n),children:w.jsxs("div",{className:Xe("flex flex-col justify-center items-center -translate-y-[70px]",r),children:[w.jsx("img",{className:Xe("size-[330px] pointer-events-none rounded-full",i),src:s||"empty-state.svg",alt:""}),w.jsx("h2",{className:"text-[18px] font-normal font-inter text-[#656565] mt-[30px]",children:t}),e&&w.jsx("p",{className:"text-[15px] font-normal max-w-[378px] font-inter text-[#656565] text-center mt-[10px]",children:e})]})}),QG=({filterTabs:t,setCurrFilterTabs:e,setFilterTabs:n,currFilterTabs:r})=>{const[i,s]=T.useState(!1),[o,l]=T.useState(""),c=()=>{const m={id:Date.now(),name:"Logs",def:{level:"DEBUG",types:[]}},g=[...t,m];n(g),e(m.id)},d=(m,g)=>{m.stopPropagation(),s(!0),l(g.name);function x(){const v=document.createRange(),S=window.getSelection();m.target&&(v.selectNodeContents(m.target),S?.removeAllRanges(),S?.addRange(v))}x()},f=(m,g)=>{s(!1),m.target.textContent||(m.target.textContent=o||g.name),g.name=m.target.textContent,localStorage.setItem("filters",JSON.stringify(t)),m.target.blur(),window.getSelection()?.removeAllRanges()},p=(m,g)=>{s(!1),m.target.textContent=g.name,m.target.blur()};return w.jsxs("div",{className:Xe("ps-[10px] flex gap-[8px] bg-white items-center min-h-[42px] filter-tabs border-b border-[#EDEFF3] overflow-x-auto z-10 overflow-y-visible no-scrollbar",i&&"border-[#ebecf0]"),children:[t.map(m=>w.jsx("div",{className:On("bg-[#FAFAFA] hover:bg-[#F3F5F9] border border-transparent relative rounded-[6px] text-[#A9A9A9] hover:text-[#282828]",m.id===r&&"shadow-main-inset !bg-[#FAFAFA] !text-[#282828]",m.id===r&&i&&"!border-black !shadow-none"),role:"button",onClick:()=>{s(!1),e(m.id)},children:w.jsx("div",{className:On("group flex min-h-[28px] max-w-[200px] rounded-[6px] max-h-[28px] justify-center leading-[18px] text-[15px] border border-transparent items-center border-e w-fit",m.id===r&&i&&"h-full rounded-[5px]"),children:w.jsx("div",{className:Xe("flex items-center gap-[8px] relative max-w-full"),children:w.jsx("p",{tabIndex:-1,onClick:g=>m.id===r&&d(g,m),contentEditable:m.id===r&&i,suppressContentEditableWarning:!0,onKeyDown:g=>g.key==="Enter"?f(g,m):g.key==="Escape"&&p(g,m),onBlur:g=>f(g,m),className:Xe("text-[15px] flex-1 overflow-hidden whitespace-nowrap text-ellipsis h-[28px] px-[8px] outline-none items-center border border-transparent flex !justify-start",m.id===r&&!i&&"hover:cursor-text"),children:m.name})})})},m.id)),w.jsx("div",{className:"flex gap-[10px] items-center justify-center size-[28px] min-w-[28px] w-fit sticky right-0 text-[#151515] hover:text-[#151515] bg-white hover:bg-[#f3f5f9] rounded-[6px]",role:"button",onClick:c,children:w.jsx(F4,{size:16})})]})},ZG=({event:t,sameTraceMessages:e,regenerateMessageFn:n,resendMessageFn:r,closeLogs:i,className:s,flaggedChanged:o})=>{const[l]=lt(As),[c]=lt(ws),d=t?.source==="customer",[f,p]=T.useState(null),[m,g]=T.useState(!1);T.useEffect(()=>{const v=ZD("Parlant-flags","message_flags",t?.trace_id,{name:"sessionIndex",keyPath:"sessionId"});v&&v.then(S=>{p(S?.flagValue),o?.(!!S?.flagValue)})},[t,m]);const x=e?.some(v=>v.serverStatus&&v.serverStatus!=="ready"&&v.serverStatus!=="error");return w.jsx(PA,{className:Xe("static",!t&&"!border-transparent bg-[#f5f6f8]",s),children:t&&w.jsxs("div",{className:Xe("flex items-center justify-between w-full pe-[12px]"),children:[w.jsx("div",{className:"flex",children:w.jsx("div",{role:"button",className:"p-[5px] pe-[10px]",onClick:()=>i?.(),children:w.jsx(gl,{height:25,width:25})})}),w.jsxs("div",{className:"flex items-center gap-[12px] mb-[1px]",children:[!d&&w.jsxs(An,{className:Xe("gap-1",f&&"border-[#9B0360] !text-[#9B0360]"),variant:"outline",onClick:()=>c.openDialog("Flag Response",w.jsx(c2,{existingFlagValue:f||"",events:e||[t],sessionId:l?.id,onFlag:()=>g(!m)}),{width:"600px",height:"636px"}),children:[w.jsx(nA,{color:f?"#9B0360":"black",size:16}),w.jsx("div",{children:f?"View Comment":"Flag"})]}),w.jsxs("button",{className:On("group bg-[#006E53] [box-shadow:0px_2px_4px_0px_#00403029,0px_1px_5.5px_0px_#006E5329] hover:bg-[#005C3F] flex h-[38px] rounded-[5px] ms-[4px] items-center gap-[7px] py-[13px] px-[10px]",x&&"opacity-50 cursor-not-allowed"),role:"button",disabled:x,onClick:()=>t?.source==="customer"?r?.(l?.id):n?.(l?.id),children:[w.jsx("img",{src:"icons/regenerate.svg",alt:"regenerate",className:"block"}),w.jsx("div",{className:"text-white text-[14px] font-normal",children:d?"Resend":"Regenerate"})]})]})]})})},xp=T.createContext(null);xp.displayName="PanelGroupContext";const xn={group:"data-panel-group",groupDirection:"data-panel-group-direction",groupId:"data-panel-group-id",panel:"data-panel",panelCollapsible:"data-panel-collapsible",panelId:"data-panel-id",panelSize:"data-panel-size",resizeHandle:"data-resize-handle",resizeHandleActive:"data-resize-handle-active",resizeHandleEnabled:"data-panel-resize-handle-enabled",resizeHandleId:"data-panel-resize-handle-id",resizeHandleState:"data-resize-handle-state"},g1=10,Vo=T.useLayoutEffect,zT=Qb.useId,JG=typeof zT=="function"?zT:()=>null;let eK=0;function b1(t=null){const e=JG(),n=T.useRef(t||e||null);return n.current===null&&(n.current=""+eK++),t??n.current}function aR({children:t,className:e="",collapsedSize:n,collapsible:r,defaultSize:i,forwardedRef:s,id:o,maxSize:l,minSize:c,onCollapse:d,onExpand:f,onResize:p,order:m,style:g,tagName:x="div",...v}){const S=T.useContext(xp);if(S===null)throw Error("Panel components must be rendered within a PanelGroup container");const{collapsePanel:C,expandPanel:A,getPanelSize:k,getPanelStyle:M,groupId:F,isPanelCollapsed:I,reevaluatePanelConstraints:D,registerPanel:G,resizePanel:X,unregisterPanel:P}=S,Y=b1(o),z=T.useRef({callbacks:{onCollapse:d,onExpand:f,onResize:p},constraints:{collapsedSize:n,collapsible:r,defaultSize:i,maxSize:l,minSize:c},id:Y,idIsFromProps:o!==void 0,order:m});T.useRef({didLogMissingDefaultSizeWarning:!1}),Vo(()=>{const{callbacks:Z,constraints:ee}=z.current,ae={...ee};z.current.id=Y,z.current.idIsFromProps=o!==void 0,z.current.order=m,Z.onCollapse=d,Z.onExpand=f,Z.onResize=p,ee.collapsedSize=n,ee.collapsible=r,ee.defaultSize=i,ee.maxSize=l,ee.minSize=c,(ae.collapsedSize!==ee.collapsedSize||ae.collapsible!==ee.collapsible||ae.maxSize!==ee.maxSize||ae.minSize!==ee.minSize)&&D(z.current,ae)}),Vo(()=>{const Z=z.current;return G(Z),()=>{P(Z)}},[m,Y,G,P]),T.useImperativeHandle(s,()=>({collapse:()=>{C(z.current)},expand:Z=>{A(z.current,Z)},getId(){return Y},getSize(){return k(z.current)},isCollapsed(){return I(z.current)},isExpanded(){return!I(z.current)},resize:Z=>{X(z.current,Z)}}),[C,A,k,I,Y,X]);const ie=M(z.current,i);return T.createElement(x,{...v,children:t,className:e,id:Y,style:{...ie,...g},[xn.groupId]:F,[xn.panel]:"",[xn.panelCollapsible]:r||void 0,[xn.panelId]:Y,[xn.panelSize]:parseFloat(""+ie.flexGrow).toFixed(1)})}const lR=T.forwardRef((t,e)=>T.createElement(aR,{...t,forwardedRef:e}));aR.displayName="Panel";lR.displayName="forwardRef(Panel)";let gb=null,zf=-1,to=null;function tK(t,e){if(e){const n=(e&hR)!==0,r=(e&pR)!==0,i=(e&mR)!==0,s=(e&gR)!==0;if(n)return i?"se-resize":s?"ne-resize":"e-resize";if(r)return i?"sw-resize":s?"nw-resize":"w-resize";if(i)return"s-resize";if(s)return"n-resize"}switch(t){case"horizontal":return"ew-resize";case"intersection":return"move";case"vertical":return"ns-resize"}}function nK(){to!==null&&(document.head.removeChild(to),gb=null,to=null,zf=-1)}function a0(t,e){var n,r;const i=tK(t,e);if(gb!==i){if(gb=i,to===null&&(to=document.createElement("style"),document.head.appendChild(to)),zf>=0){var s;(s=to.sheet)===null||s===void 0||s.removeRule(zf)}zf=(n=(r=to.sheet)===null||r===void 0?void 0:r.insertRule(`*{cursor: ${i} !important;}`))!==null&&n!==void 0?n:-1}}function uR(t){return t.type==="keydown"}function cR(t){return t.type.startsWith("pointer")}function dR(t){return t.type.startsWith("mouse")}function vp(t){if(cR(t)){if(t.isPrimary)return{x:t.clientX,y:t.clientY}}else if(dR(t))return{x:t.clientX,y:t.clientY};return{x:1/0,y:1/0}}function rK(){if(typeof matchMedia=="function")return matchMedia("(pointer:coarse)").matches?"coarse":"fine"}function iK(t,e,n){return t.xe.x&&t.ye.y}function sK(t,e){if(t===e)throw new Error("Cannot compare node with itself");const n={a:WT(t),b:WT(e)};let r;for(;n.a.at(-1)===n.b.at(-1);)t=n.a.pop(),e=n.b.pop(),r=t;gt(r,"Stacking order can only be calculated for elements with a common ancestor");const i={a:$T(jT(n.a)),b:$T(jT(n.b))};if(i.a===i.b){const s=r.childNodes,o={a:n.a.at(-1),b:n.b.at(-1)};let l=s.length;for(;l--;){const c=s[l];if(c===o.a)return 1;if(c===o.b)return-1}}return Math.sign(i.a-i.b)}const oK=/\b(?:position|zIndex|opacity|transform|webkitTransform|mixBlendMode|filter|webkitFilter|isolation)\b/;function aK(t){var e;const n=getComputedStyle((e=fR(t))!==null&&e!==void 0?e:t).display;return n==="flex"||n==="inline-flex"}function lK(t){const e=getComputedStyle(t);return!!(e.position==="fixed"||e.zIndex!=="auto"&&(e.position!=="static"||aK(t))||+e.opacity<1||"transform"in e&&e.transform!=="none"||"webkitTransform"in e&&e.webkitTransform!=="none"||"mixBlendMode"in e&&e.mixBlendMode!=="normal"||"filter"in e&&e.filter!=="none"||"webkitFilter"in e&&e.webkitFilter!=="none"||"isolation"in e&&e.isolation==="isolate"||oK.test(e.willChange)||e.webkitOverflowScrolling==="touch")}function jT(t){let e=t.length;for(;e--;){const n=t[e];if(gt(n,"Missing node"),lK(n))return n}return null}function $T(t){return t&&Number(getComputedStyle(t).zIndex)||0}function WT(t){const e=[];for(;t;)e.push(t),t=fR(t);return e}function fR(t){const{parentNode:e}=t;return e&&e instanceof ShadowRoot?e.host:e}const hR=1,pR=2,mR=4,gR=8,uK=rK()==="coarse";let wi=[],ll=!1,Bo=new Map,wp=new Map;const gc=new Set;function cK(t,e,n,r,i){var s;const{ownerDocument:o}=e,l={direction:n,element:e,hitAreaMargins:r,setResizeHandlerState:i},c=(s=Bo.get(o))!==null&&s!==void 0?s:0;return Bo.set(o,c+1),gc.add(l),Eh(),function(){var f;wp.delete(t),gc.delete(l);const p=(f=Bo.get(o))!==null&&f!==void 0?f:1;if(Bo.set(o,p-1),Eh(),p===1&&Bo.delete(o),wi.includes(l)){const m=wi.indexOf(l);m>=0&&wi.splice(m,1),y1(),i("up",!0,null)}}}function dK(t){const{target:e}=t,{x:n,y:r}=vp(t);ll=!0,E1({target:e,x:n,y:r}),Eh(),wi.length>0&&(yh("down",t),t.preventDefault(),bR(e)||t.stopImmediatePropagation())}function l0(t){const{x:e,y:n}=vp(t);if(ll&&t.buttons===0&&(ll=!1,yh("up",t)),!ll){const{target:r}=t;E1({target:r,x:e,y:n})}yh("move",t),y1(),wi.length>0&&t.preventDefault()}function u0(t){const{target:e}=t,{x:n,y:r}=vp(t);wp.clear(),ll=!1,wi.length>0&&(t.preventDefault(),bR(e)||t.stopImmediatePropagation()),yh("up",t),E1({target:e,x:n,y:r}),y1(),Eh()}function bR(t){let e=t;for(;e;){if(e.hasAttribute(xn.resizeHandle))return!0;e=e.parentElement}return!1}function E1({target:t,x:e,y:n}){wi.splice(0);let r=null;(t instanceof HTMLElement||t instanceof SVGElement)&&(r=t),gc.forEach(i=>{const{element:s,hitAreaMargins:o}=i,l=s.getBoundingClientRect(),{bottom:c,left:d,right:f,top:p}=l,m=uK?o.coarse:o.fine;if(e>=d-m&&e<=f+m&&n>=p-m&&n<=c+m){if(r!==null&&document.contains(r)&&s!==r&&!s.contains(r)&&!r.contains(s)&&sK(r,s)>0){let x=r,v=!1;for(;x&&!x.contains(s);){if(iK(x.getBoundingClientRect(),l)){v=!0;break}x=x.parentElement}if(v)return}wi.push(i)}})}function c0(t,e){wp.set(t,e)}function y1(){let t=!1,e=!1;wi.forEach(r=>{const{direction:i}=r;i==="horizontal"?t=!0:e=!0});let n=0;wp.forEach(r=>{n|=r}),t&&e?a0("intersection",n):t?a0("horizontal",n):e?a0("vertical",n):nK()}let d0=new AbortController;function Eh(){d0.abort(),d0=new AbortController;const t={capture:!0,signal:d0.signal};gc.size&&(ll?(wi.length>0&&Bo.forEach((e,n)=>{const{body:r}=n;e>0&&(r.addEventListener("contextmenu",u0,t),r.addEventListener("pointerleave",l0,t),r.addEventListener("pointermove",l0,t))}),window.addEventListener("pointerup",u0,t),window.addEventListener("pointercancel",u0,t)):Bo.forEach((e,n)=>{const{body:r}=n;e>0&&(r.addEventListener("pointerdown",dK,t),r.addEventListener("pointermove",l0,t))}))}function yh(t,e){gc.forEach(n=>{const{setResizeHandlerState:r}=n,i=wi.includes(n);r(t,i,e)})}function fK(){const[t,e]=T.useState(0);return T.useCallback(()=>e(n=>n+1),[])}function gt(t,e){if(!t)throw console.error(e),Error(e)}function Jo(t,e,n=g1){return t.toFixed(n)===e.toFixed(n)?0:t>e?1:-1}function ps(t,e,n=g1){return Jo(t,e,n)===0}function zr(t,e,n){return Jo(t,e,n)===0}function hK(t,e,n){if(t.length!==e.length)return!1;for(let r=0;r0&&(t=t<0?0-C:C)}}}{const p=t<0?l:c,m=n[p];gt(m,`No panel constraints found for index ${p}`);const{collapsedSize:g=0,collapsible:x,minSize:v=0}=m;if(x){const S=e[p];if(gt(S!=null,`Previous layout not found for panel index ${p}`),zr(S,v)){const C=S-g;Jo(C,Math.abs(t))>0&&(t=t<0?0-C:C)}}}}{const p=t<0?1:-1;let m=t<0?c:l,g=0;for(;;){const v=e[m];gt(v!=null,`Previous layout not found for panel index ${m}`);const C=Ja({panelConstraints:n,panelIndex:m,size:100})-v;if(g+=C,m+=p,m<0||m>=n.length)break}const x=Math.min(Math.abs(t),Math.abs(g));t=t<0?0-x:x}{let m=t<0?l:c;for(;m>=0&&m=0))break;t<0?m--:m++}}if(hK(i,o))return i;{const p=t<0?c:l,m=e[p];gt(m!=null,`Previous layout not found for panel index ${p}`);const g=m+d,x=Ja({panelConstraints:n,panelIndex:p,size:g});if(o[p]=x,!zr(x,g)){let v=g-x,C=t<0?c:l;for(;C>=0&&C0?C--:C++}}}const f=o.reduce((p,m)=>m+p,0);return zr(f,100)?o:i}function pK({layout:t,panelsArray:e,pivotIndices:n}){let r=0,i=100,s=0,o=0;const l=n[0];gt(l!=null,"No pivot index found"),e.forEach((p,m)=>{const{constraints:g}=p,{maxSize:x=100,minSize:v=0}=g;m===l?(r=v,i=x):(s+=v,o+=x)});const c=Math.min(i,100-s),d=Math.max(r,100-o),f=t[l];return{valueMax:c,valueMin:d,valueNow:f}}function bc(t,e=document){return Array.from(e.querySelectorAll(`[${xn.resizeHandleId}][data-panel-group-id="${t}"]`))}function ER(t,e,n=document){const i=bc(t,n).findIndex(s=>s.getAttribute(xn.resizeHandleId)===e);return i??null}function yR(t,e,n){const r=ER(t,e,n);return r!=null?[r,r+1]:[-1,-1]}function xR(t,e=document){var n;if(e instanceof HTMLElement&&(e==null||(n=e.dataset)===null||n===void 0?void 0:n.panelGroupId)==t)return e;const r=e.querySelector(`[data-panel-group][data-panel-group-id="${t}"]`);return r||null}function Tp(t,e=document){const n=e.querySelector(`[${xn.resizeHandleId}="${t}"]`);return n||null}function mK(t,e,n,r=document){var i,s,o,l;const c=Tp(e,r),d=bc(t,r),f=c?d.indexOf(c):-1,p=(i=(s=n[f])===null||s===void 0?void 0:s.id)!==null&&i!==void 0?i:null,m=(o=(l=n[f+1])===null||l===void 0?void 0:l.id)!==null&&o!==void 0?o:null;return[p,m]}function gK({committedValuesRef:t,eagerValuesRef:e,groupId:n,layout:r,panelDataArray:i,panelGroupElement:s,setLayout:o}){T.useRef({didWarnAboutMissingResizeHandle:!1}),Vo(()=>{if(!s)return;const l=bc(n,s);for(let c=0;c{l.forEach((c,d)=>{c.removeAttribute("aria-controls"),c.removeAttribute("aria-valuemax"),c.removeAttribute("aria-valuemin"),c.removeAttribute("aria-valuenow")})}},[n,r,i,s]),T.useEffect(()=>{if(!s)return;const l=e.current;gt(l,"Eager values not found");const{panelDataArray:c}=l,d=xR(n,s);gt(d!=null,`No group found for id "${n}"`);const f=bc(n,s);gt(f,`No resize handles found for group id "${n}"`);const p=f.map(m=>{const g=m.getAttribute(xn.resizeHandleId);gt(g,"Resize handle element has no handle id attribute");const[x,v]=mK(n,g,c,s);if(x==null||v==null)return()=>{};const S=C=>{if(!C.defaultPrevented)switch(C.key){case"Enter":{C.preventDefault();const A=c.findIndex(k=>k.id===x);if(A>=0){const k=c[A];gt(k,`No panel data found for index ${A}`);const M=r[A],{collapsedSize:F=0,collapsible:I,minSize:D=0}=k.constraints;if(M!=null&&I){const G=zu({delta:zr(M,F)?D-F:F-M,initialLayout:r,panelConstraints:c.map(X=>X.constraints),pivotIndices:yR(n,g,s),prevLayout:r,trigger:"keyboard"});r!==G&&o(G)}}break}}};return m.addEventListener("keydown",S),()=>{m.removeEventListener("keydown",S)}});return()=>{p.forEach(m=>m())}},[s,t,e,n,r,i,o])}function VT(t,e){if(t.length!==e.length)return!1;for(let n=0;ns.constraints);let r=0,i=100;for(let s=0;s{const s=t[i];gt(s,`Panel data not found for index ${i}`);const{callbacks:o,constraints:l,id:c}=s,{collapsedSize:d=0,collapsible:f}=l,p=n[c];if(p==null||r!==p){n[c]=r;const{onCollapse:m,onExpand:g,onResize:x}=o;x&&x(r,p),f&&(m||g)&&(g&&(p==null||ps(p,d))&&!ps(r,d)&&g(),m&&(p==null||!ps(p,d))&&ps(r,d)&&m())}})}function yf(t,e){if(t.length!==e.length)return!1;for(let n=0;n{n!==null&&clearTimeout(n),n=setTimeout(()=>{t(...i)},e)}}function GT(t){try{if(typeof localStorage<"u")t.getItem=e=>localStorage.getItem(e),t.setItem=(e,n)=>{localStorage.setItem(e,n)};else throw new Error("localStorage not supported in this environment")}catch(e){console.error(e),t.getItem=()=>null,t.setItem=()=>{}}}function wR(t){return`react-resizable-panels:${t}`}function TR(t){return t.map(e=>{const{constraints:n,id:r,idIsFromProps:i,order:s}=e;return i?r:s?`${s}:${JSON.stringify(n)}`:JSON.stringify(n)}).sort((e,n)=>e.localeCompare(n)).join(",")}function SR(t,e){try{const n=wR(t),r=e.getItem(n);if(r){const i=JSON.parse(r);if(typeof i=="object"&&i!=null)return i}}catch{}return null}function wK(t,e,n){var r,i;const s=(r=SR(t,n))!==null&&r!==void 0?r:{},o=TR(e);return(i=s[o])!==null&&i!==void 0?i:null}function TK(t,e,n,r,i){var s;const o=wR(t),l=TR(e),c=(s=SR(t,i))!==null&&s!==void 0?s:{};c[l]={expandToSizes:Object.fromEntries(n.entries()),layout:r};try{i.setItem(o,JSON.stringify(c))}catch(d){console.error(d)}}function KT({layout:t,panelConstraints:e}){const n=[...t],r=n.reduce((s,o)=>s+o,0);if(n.length!==e.length)throw Error(`Invalid ${e.length} panel layout: ${n.map(s=>`${s}%`).join(", ")}`);if(!zr(r,100)&&n.length>0)for(let s=0;s(GT(ju),ju.getItem(t)),setItem:(t,e)=>{GT(ju),ju.setItem(t,e)}},YT={};function _R({autoSaveId:t=null,children:e,className:n="",direction:r,forwardedRef:i,id:s=null,onLayout:o=null,keyboardResizeBy:l=null,storage:c=ju,style:d,tagName:f="div",...p}){const m=b1(s),g=T.useRef(null),[x,v]=T.useState(null),[S,C]=T.useState([]),A=fK(),k=T.useRef({}),M=T.useRef(new Map),F=T.useRef(0),I=T.useRef({autoSaveId:t,direction:r,dragState:x,id:m,keyboardResizeBy:l,onLayout:o,storage:c}),D=T.useRef({layout:S,panelDataArray:[],panelDataArrayChanged:!1});T.useRef({didLogIdAndOrderWarning:!1,didLogPanelConstraintsWarning:!1,prevPanelIds:[]}),T.useImperativeHandle(i,()=>({getId:()=>I.current.id,getLayout:()=>{const{layout:R}=D.current;return R},setLayout:R=>{const{onLayout:oe}=I.current,{layout:pe,panelDataArray:ue}=D.current,J=KT({layout:R,panelConstraints:ue.map(he=>he.constraints)});VT(pe,J)||(C(J),D.current.layout=J,oe&&oe(J),$a(ue,J,k.current))}}),[]),Vo(()=>{I.current.autoSaveId=t,I.current.direction=r,I.current.dragState=x,I.current.id=m,I.current.onLayout=o,I.current.storage=c}),gK({committedValuesRef:I,eagerValuesRef:D,groupId:m,layout:S,panelDataArray:D.current.panelDataArray,setLayout:C,panelGroupElement:g.current}),T.useEffect(()=>{const{panelDataArray:R}=D.current;if(t){if(S.length===0||S.length!==R.length)return;let oe=YT[t];oe==null&&(oe=vK(TK,SK),YT[t]=oe);const pe=[...R],ue=new Map(M.current);oe(t,pe,ue,S,c)}},[t,S,c]),T.useEffect(()=>{});const G=T.useCallback(R=>{const{onLayout:oe}=I.current,{layout:pe,panelDataArray:ue}=D.current;if(R.constraints.collapsible){const J=ue.map(Ve=>Ve.constraints),{collapsedSize:he=0,panelSize:_e,pivotIndices:ke}=Fo(ue,R,pe);if(gt(_e!=null,`Panel size not found for panel "${R.id}"`),!ps(_e,he)){M.current.set(R.id,_e);const ot=Ya(ue,R)===ue.length-1?_e-he:he-_e,qe=zu({delta:ot,initialLayout:pe,panelConstraints:J,pivotIndices:ke,prevLayout:pe,trigger:"imperative-api"});yf(pe,qe)||(C(qe),D.current.layout=qe,oe&&oe(qe),$a(ue,qe,k.current))}}},[]),X=T.useCallback((R,oe)=>{const{onLayout:pe}=I.current,{layout:ue,panelDataArray:J}=D.current;if(R.constraints.collapsible){const he=J.map(kt=>kt.constraints),{collapsedSize:_e=0,panelSize:ke=0,minSize:Ve=0,pivotIndices:ot}=Fo(J,R,ue),qe=oe??Ve;if(ps(ke,_e)){const kt=M.current.get(R.id),fn=kt!=null&&kt>=qe?kt:qe,Yt=Ya(J,R)===J.length-1?ke-fn:fn-ke,Ct=zu({delta:Yt,initialLayout:ue,panelConstraints:he,pivotIndices:ot,prevLayout:ue,trigger:"imperative-api"});yf(ue,Ct)||(C(Ct),D.current.layout=Ct,pe&&pe(Ct),$a(J,Ct,k.current))}}},[]),P=T.useCallback(R=>{const{layout:oe,panelDataArray:pe}=D.current,{panelSize:ue}=Fo(pe,R,oe);return gt(ue!=null,`Panel size not found for panel "${R.id}"`),ue},[]),Y=T.useCallback((R,oe)=>{const{panelDataArray:pe}=D.current,ue=Ya(pe,R);return xK({defaultSize:oe,dragState:x,layout:S,panelData:pe,panelIndex:ue})},[x,S]),z=T.useCallback(R=>{const{layout:oe,panelDataArray:pe}=D.current,{collapsedSize:ue=0,collapsible:J,panelSize:he}=Fo(pe,R,oe);return gt(he!=null,`Panel size not found for panel "${R.id}"`),J===!0&&ps(he,ue)},[]),ie=T.useCallback(R=>{const{layout:oe,panelDataArray:pe}=D.current,{collapsedSize:ue=0,collapsible:J,panelSize:he}=Fo(pe,R,oe);return gt(he!=null,`Panel size not found for panel "${R.id}"`),!J||Jo(he,ue)>0},[]),Z=T.useCallback(R=>{const{panelDataArray:oe}=D.current;oe.push(R),oe.sort((pe,ue)=>{const J=pe.order,he=ue.order;return J==null&&he==null?0:J==null?-1:he==null?1:J-he}),D.current.panelDataArrayChanged=!0,A()},[A]);Vo(()=>{if(D.current.panelDataArrayChanged){D.current.panelDataArrayChanged=!1;const{autoSaveId:R,onLayout:oe,storage:pe}=I.current,{layout:ue,panelDataArray:J}=D.current;let he=null;if(R){const ke=wK(R,J,pe);ke&&(M.current=new Map(Object.entries(ke.expandToSizes)),he=ke.layout)}he==null&&(he=yK({panelDataArray:J}));const _e=KT({layout:he,panelConstraints:J.map(ke=>ke.constraints)});VT(ue,_e)||(C(_e),D.current.layout=_e,oe&&oe(_e),$a(J,_e,k.current))}}),Vo(()=>{const R=D.current;return()=>{R.layout=[]}},[]);const ee=T.useCallback(R=>{let oe=!1;const pe=g.current;return pe&&window.getComputedStyle(pe,null).getPropertyValue("direction")==="rtl"&&(oe=!0),function(J){J.preventDefault();const he=g.current;if(!he)return()=>null;const{direction:_e,dragState:ke,id:Ve,keyboardResizeBy:ot,onLayout:qe}=I.current,{layout:kt,panelDataArray:fn}=D.current,{initialLayout:nt}=ke??{},Yt=yR(Ve,R,he);let Ct=EK(J,R,_e,ke,ot,he);const Pn=_e==="horizontal";Pn&&oe&&(Ct=-Ct);const Fn=fn.map(Mn=>Mn.constraints),on=zu({delta:Ct,initialLayout:nt??kt,panelConstraints:Fn,pivotIndices:Yt,prevLayout:kt,trigger:uR(J)?"keyboard":"mouse-or-touch"}),dr=!yf(kt,on);(cR(J)||dR(J))&&F.current!=Ct&&(F.current=Ct,!dr&&Ct!==0?Pn?c0(R,Ct<0?hR:pR):c0(R,Ct<0?mR:gR):c0(R,0)),dr&&(C(on),D.current.layout=on,qe&&qe(on),$a(fn,on,k.current))}},[]),ae=T.useCallback((R,oe)=>{const{onLayout:pe}=I.current,{layout:ue,panelDataArray:J}=D.current,he=J.map(kt=>kt.constraints),{panelSize:_e,pivotIndices:ke}=Fo(J,R,ue);gt(_e!=null,`Panel size not found for panel "${R.id}"`);const ot=Ya(J,R)===J.length-1?_e-oe:oe-_e,qe=zu({delta:ot,initialLayout:ue,panelConstraints:he,pivotIndices:ke,prevLayout:ue,trigger:"imperative-api"});yf(ue,qe)||(C(qe),D.current.layout=qe,pe&&pe(qe),$a(J,qe,k.current))},[]),de=T.useCallback((R,oe)=>{const{layout:pe,panelDataArray:ue}=D.current,{collapsedSize:J=0,collapsible:he}=oe,{collapsedSize:_e=0,collapsible:ke,maxSize:Ve=100,minSize:ot=0}=R.constraints,{panelSize:qe}=Fo(ue,R,pe);qe!=null&&(he&&ke&&ps(qe,J)?ps(J,_e)||ae(R,_e):qeVe&&ae(R,Ve))},[ae]),j=T.useCallback((R,oe)=>{const{direction:pe}=I.current,{layout:ue}=D.current;if(!g.current)return;const J=Tp(R,g.current);gt(J,`Drag handle element not found for id "${R}"`);const he=vR(pe,oe);v({dragHandleId:R,dragHandleRect:J.getBoundingClientRect(),initialCursorPosition:he,initialLayout:ue})},[]),W=T.useCallback(()=>{v(null)},[]),O=T.useCallback(R=>{const{panelDataArray:oe}=D.current,pe=Ya(oe,R);pe>=0&&(oe.splice(pe,1),delete k.current[R.id],D.current.panelDataArrayChanged=!0,A())},[A]),U=T.useMemo(()=>({collapsePanel:G,direction:r,dragState:x,expandPanel:X,getPanelSize:P,getPanelStyle:Y,groupId:m,isPanelCollapsed:z,isPanelExpanded:ie,reevaluatePanelConstraints:de,registerPanel:Z,registerResizeHandle:ee,resizePanel:ae,startDragging:j,stopDragging:W,unregisterPanel:O,panelGroupElement:g.current}),[G,x,r,X,P,Y,m,z,ie,de,Z,ee,ae,j,W,O]),Q={display:"flex",flexDirection:r==="horizontal"?"row":"column",height:"100%",overflow:"hidden",width:"100%"};return T.createElement(xp.Provider,{value:U},T.createElement(f,{...p,children:e,className:n,id:s,ref:g,style:{...Q,...d},[xn.group]:"",[xn.groupDirection]:r,[xn.groupId]:m}))}const CR=T.forwardRef((t,e)=>T.createElement(_R,{...t,forwardedRef:e}));_R.displayName="PanelGroup";CR.displayName="forwardRef(PanelGroup)";function Ya(t,e){return t.findIndex(n=>n===e||n.id===e.id)}function Fo(t,e,n){const r=Ya(t,e),s=r===t.length-1?[r-1,r]:[r,r+1],o=n[r];return{...e.constraints,panelSize:o,pivotIndices:s}}function _K({disabled:t,handleId:e,resizeHandler:n,panelGroupElement:r}){T.useEffect(()=>{if(t||n==null||r==null)return;const i=Tp(e,r);if(i==null)return;const s=o=>{if(!o.defaultPrevented)switch(o.key){case"ArrowDown":case"ArrowLeft":case"ArrowRight":case"ArrowUp":case"End":case"Home":{o.preventDefault(),n(o);break}case"F6":{o.preventDefault();const l=i.getAttribute(xn.groupId);gt(l,`No group element found for id "${l}"`);const c=bc(l,r),d=ER(l,e,r);gt(d!==null,`No resize element found for id "${e}"`);const f=o.shiftKey?d>0?d-1:c.length-1:d+1{i.removeEventListener("keydown",s)}},[r,t,e,n])}function AR({children:t=null,className:e="",disabled:n=!1,hitAreaMargins:r,id:i,onBlur:s,onClick:o,onDragging:l,onFocus:c,onPointerDown:d,onPointerUp:f,style:p={},tabIndex:m=0,tagName:g="div",...x}){var v,S;const C=T.useRef(null),A=T.useRef({onClick:o,onDragging:l,onPointerDown:d,onPointerUp:f});T.useEffect(()=>{A.current.onClick=o,A.current.onDragging=l,A.current.onPointerDown=d,A.current.onPointerUp=f});const k=T.useContext(xp);if(k===null)throw Error("PanelResizeHandle components must be rendered within a PanelGroup container");const{direction:M,groupId:F,registerResizeHandle:I,startDragging:D,stopDragging:G,panelGroupElement:X}=k,P=b1(i),[Y,z]=T.useState("inactive"),[ie,Z]=T.useState(!1),[ee,ae]=T.useState(null),de=T.useRef({state:Y});Vo(()=>{de.current.state=Y}),T.useEffect(()=>{if(n)ae(null);else{const U=I(P);ae(()=>U)}},[n,P,I]);const j=(v=r?.coarse)!==null&&v!==void 0?v:15,W=(S=r?.fine)!==null&&S!==void 0?S:5;T.useEffect(()=>{if(n||ee==null)return;const U=C.current;gt(U,"Element ref not attached");let Q=!1;return cK(P,U,M,{coarse:j,fine:W},(oe,pe,ue)=>{if(!pe){z("inactive");return}switch(oe){case"down":{z("drag"),Q=!1,gt(ue,'Expected event to be defined for "down" action'),D(P,ue);const{onDragging:J,onPointerDown:he}=A.current;J?.(!0),he?.();break}case"move":{const{state:J}=de.current;Q=!0,J!=="drag"&&z("hover"),gt(ue,'Expected event to be defined for "move" action'),ee(ue);break}case"up":{z("hover"),G();const{onClick:J,onDragging:he,onPointerUp:_e}=A.current;he?.(!1),_e?.(),Q||J?.();break}}})},[j,M,n,W,I,P,ee,D,G]),_K({disabled:n,handleId:P,resizeHandler:ee,panelGroupElement:X});const O={touchAction:"none",userSelect:"none"};return T.createElement(g,{...x,children:t,className:e,id:i,onBlur:()=>{Z(!1),s?.()},onFocus:()=>{Z(!0),c?.()},ref:C,role:"separator",style:{...O,...p},tabIndex:m,[xn.groupDirection]:M,[xn.groupId]:F,[xn.resizeHandle]:"",[xn.resizeHandleActive]:Y==="drag"?"pointer":ie?"keyboard":void 0,[xn.resizeHandleEnabled]:!n,[xn.resizeHandleId]:P,[xn.resizeHandleState]:Y})}AR.displayName="PanelResizeHandle";const CK=({className:t,...e})=>w.jsx(CR,{className:Rt("flex h-full w-full data-[panel-group-direction=vertical]:flex-col",t),...e}),qT=lR,AK=({withHandle:t,className:e,...n})=>w.jsx(AR,{className:Rt("relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",e),...n,children:t&&w.jsx("div",{className:"z-10 flex h-4 w-3 items-center justify-center rounded-sm",children:w.jsx("img",{src:"icons/resize.svg",className:"rotate-90 max-w-[unset]",alt:""})})});let bb=[],kR=[];(()=>{let t="lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map(e=>e?parseInt(e,36):1);for(let e=0,n=0;e>1;if(t=kR[r])e=r+1;else return!0;if(e==n)return!1}}function XT(t){return t>=127462&&t<=127487}const QT=8205;function NK(t,e,n=!0,r=!0){return(n?NR:RK)(t,e,r)}function NR(t,e,n){if(e==t.length)return e;e&&RR(t.charCodeAt(e))&&IR(t.charCodeAt(e-1))&&e--;let r=f0(t,e);for(e+=ZT(r);e=0&&XT(f0(t,o));)s++,o-=2;if(s%2==0)break;e+=2}else break}return e}function RK(t,e,n){for(;e>0;){let r=NR(t,e-2,n);if(r=56320&&t<57344}function IR(t){return t>=55296&&t<56320}function ZT(t){return t<65536?1:2}class Lt{lineAt(e){if(e<0||e>this.length)throw new RangeError(`Invalid position ${e} in document of length ${this.length}`);return this.lineInner(e,!1,1,0)}line(e){if(e<1||e>this.lines)throw new RangeError(`Invalid line number ${e} in ${this.lines}-line document`);return this.lineInner(e,!0,1,0)}replace(e,n,r){[e,n]=xl(this,e,n);let i=[];return this.decompose(0,e,i,2),r.length&&r.decompose(0,r.length,i,3),this.decompose(n,this.length,i,1),$i.from(i,this.length-(n-e)+r.length)}append(e){return this.replace(this.length,this.length,e)}slice(e,n=this.length){[e,n]=xl(this,e,n);let r=[];return this.decompose(e,n,r,0),$i.from(r,n-e)}eq(e){if(e==this)return!0;if(e.length!=this.length||e.lines!=this.lines)return!1;let n=this.scanIdentical(e,1),r=this.length-this.scanIdentical(e,-1),i=new ec(this),s=new ec(e);for(let o=n,l=n;;){if(i.next(o),s.next(o),o=0,i.lineBreak!=s.lineBreak||i.done!=s.done||i.value!=s.value)return!1;if(l+=i.value.length,i.done||l>=r)return!0}}iter(e=1){return new ec(this,e)}iterRange(e,n=this.length){return new OR(this,e,n)}iterLines(e,n){let r;if(e==null)r=this.iter();else{n==null&&(n=this.lines+1);let i=this.line(e).from;r=this.iterRange(i,Math.max(i,n==this.lines+1?this.length:n<=1?0:this.line(n-1).to))}return new MR(r)}toString(){return this.sliceString(0)}toJSON(){let e=[];return this.flatten(e),e}constructor(){}static of(e){if(e.length==0)throw new RangeError("A document must have at least one line");return e.length==1&&!e[0]?Lt.empty:e.length<=32?new Sn(e):$i.from(Sn.split(e,[]))}}class Sn extends Lt{constructor(e,n=IK(e)){super(),this.text=e,this.length=n}get lines(){return this.text.length}get children(){return null}lineInner(e,n,r,i){for(let s=0;;s++){let o=this.text[s],l=i+o.length;if((n?r:l)>=e)return new OK(i,l,r,o);i=l+1,r++}}decompose(e,n,r,i){let s=e<=0&&n>=this.length?this:new Sn(JT(this.text,e,n),Math.min(n,this.length)-Math.max(0,e));if(i&1){let o=r.pop(),l=jf(s.text,o.text.slice(),0,s.length);if(l.length<=32)r.push(new Sn(l,o.length+s.length));else{let c=l.length>>1;r.push(new Sn(l.slice(0,c)),new Sn(l.slice(c)))}}else r.push(s)}replace(e,n,r){if(!(r instanceof Sn))return super.replace(e,n,r);[e,n]=xl(this,e,n);let i=jf(this.text,jf(r.text,JT(this.text,0,e)),n),s=this.length+r.length-(n-e);return i.length<=32?new Sn(i,s):$i.from(Sn.split(i,[]),s)}sliceString(e,n=this.length,r=` `){[e,n]=xl(this,e,n);let i="";for(let s=0,o=0;s<=n&&oe&&o&&(i+=r),es&&(i+=l.slice(Math.max(0,e-s),n-s)),s=c+1}return i}flatten(e){for(let n of this.text)e.push(n)}scanIdentical(){return 0}static split(e,n){let r=[],i=-1;for(let s of e)r.push(s),i+=s.length+1,r.length==32&&(n.push(new Sn(r,i)),r=[],i=-1);return i>-1&&n.push(new Sn(r,i)),n}}class $i extends Lt{constructor(e,n){super(),this.children=e,this.length=n,this.lines=0;for(let r of e)this.lines+=r.lines}lineInner(e,n,r,i){for(let s=0;;s++){let o=this.children[s],l=i+o.length,c=r+o.lines-1;if((n?c:l)>=e)return o.lineInner(e,n,r,i);i=l+1,r=c+1}}decompose(e,n,r,i){for(let s=0,o=0;o<=n&&s=o){let d=i&((o<=e?1:0)|(c>=n?2:0));o>=e&&c<=n&&!d?r.push(l):l.decompose(e-o,n-o,r,d)}o=c+1}}replace(e,n,r){if([e,n]=xl(this,e,n),r.lines=s&&n<=l){let c=o.replace(e-s,n-s,r),d=this.lines-o.lines+c.lines;if(c.lines>4&&c.lines>d>>6){let f=this.children.slice();return f[i]=c,new $i(f,this.length-(n-e)+r.length)}return super.replace(s,l,c)}s=l+1}return super.replace(e,n,r)}sliceString(e,n=this.length,r=` `){[e,n]=xl(this,e,n);let i="";for(let s=0,o=0;se&&s&&(i+=r),eo&&(i+=l.sliceString(e-o,n-o,r)),o=c+1}return i}flatten(e){for(let n of this.children)n.flatten(e)}scanIdentical(e,n){if(!(e instanceof $i))return 0;let r=0,[i,s,o,l]=n>0?[0,0,this.children.length,e.children.length]:[this.children.length-1,e.children.length-1,-1,-1];for(;;i+=n,s+=n){if(i==o||s==l)return r;let c=this.children[i],d=e.children[s];if(c!=d)return r+c.scanIdentical(d,n);r+=c.length+1}}static from(e,n=e.reduce((r,i)=>r+i.length+1,-1)){let r=0;for(let g of e)r+=g.lines;if(r<32){let g=[];for(let x of e)x.flatten(g);return new Sn(g,n)}let i=Math.max(32,r>>5),s=i<<1,o=i>>1,l=[],c=0,d=-1,f=[];function p(g){let x;if(g.lines>s&&g instanceof $i)for(let v of g.children)p(v);else g.lines>o&&(c>o||!c)?(m(),l.push(g)):g instanceof Sn&&c&&(x=f[f.length-1])instanceof Sn&&g.lines+x.lines<=32?(c+=g.lines,d+=g.length+1,f[f.length-1]=new Sn(x.text.concat(g.text),x.length+1+g.length)):(c+g.lines>i&&m(),c+=g.lines,d+=g.length+1,f.push(g))}function m(){c!=0&&(l.push(f.length==1?f[0]:$i.from(f,d)),d=-1,c=f.length=0)}for(let g of e)p(g);return m(),l.length==1?l[0]:new $i(l,n)}}Lt.empty=new Sn([""],0);function IK(t){let e=-1;for(let n of t)e+=n.length+1;return e}function jf(t,e,n=0,r=1e9){for(let i=0,s=0,o=!0;s=n&&(c>r&&(l=l.slice(0,r-i)),i0?1:(e instanceof Sn?e.text.length:e.children.length)<<1]}nextInner(e,n){for(this.done=this.lineBreak=!1;;){let r=this.nodes.length-1,i=this.nodes[r],s=this.offsets[r],o=s>>1,l=i instanceof Sn?i.text.length:i.children.length;if(o==(n>0?l:0)){if(r==0)return this.done=!0,this.value="",this;n>0&&this.offsets[r-1]++,this.nodes.pop(),this.offsets.pop()}else if((s&1)==(n>0?0:1)){if(this.offsets[r]+=n,e==0)return this.lineBreak=!0,this.value=` `,this;e--}else if(i instanceof Sn){let c=i.text[o+(n<0?-1:0)];if(this.offsets[r]+=n,c.length>Math.max(0,e))return this.value=e==0?c:n>0?c.slice(e):c.slice(0,c.length-e),this;e-=c.length}else{let c=i.children[o+(n<0?-1:0)];e>c.length?(e-=c.length,this.offsets[r]+=n):(n<0&&this.offsets[r]--,this.nodes.push(c),this.offsets.push(n>0?1:(c instanceof Sn?c.text.length:c.children.length)<<1))}}}next(e=0){return e<0&&(this.nextInner(-e,-this.dir),e=this.value.length),this.nextInner(e,this.dir)}}class OR{constructor(e,n,r){this.value="",this.done=!1,this.cursor=new ec(e,n>r?-1:1),this.pos=n>r?e.length:0,this.from=Math.min(n,r),this.to=Math.max(n,r)}nextInner(e,n){if(n<0?this.pos<=this.from:this.pos>=this.to)return this.value="",this.done=!0,this;e+=Math.max(0,n<0?this.pos-this.to:this.from-this.pos);let r=n<0?this.pos-this.from:this.to-this.pos;e>r&&(e=r),r-=e;let{value:i}=this.cursor.next(e);return this.pos+=(i.length+e)*n,this.value=i.length<=r?i:n<0?i.slice(i.length-r):i.slice(0,r),this.done=!this.value,this}next(e=0){return e<0?e=Math.max(e,this.from-this.pos):e>0&&(e=Math.min(e,this.to-this.pos)),this.nextInner(e,this.cursor.dir)}get lineBreak(){return this.cursor.lineBreak&&this.value!=""}}class MR{constructor(e){this.inner=e,this.afterBreak=!0,this.value="",this.done=!1}next(e=0){let{done:n,lineBreak:r,value:i}=this.inner.next(e);return n&&this.afterBreak?(this.value="",this.afterBreak=!1):n?(this.done=!0,this.value=""):r?this.afterBreak?this.value="":(this.afterBreak=!0,this.next()):(this.value=i,this.afterBreak=!1),this}get lineBreak(){return!1}}typeof Symbol<"u"&&(Lt.prototype[Symbol.iterator]=function(){return this.iter()},ec.prototype[Symbol.iterator]=OR.prototype[Symbol.iterator]=MR.prototype[Symbol.iterator]=function(){return this});class OK{constructor(e,n,r,i){this.from=e,this.to=n,this.number=r,this.text=i}get length(){return this.to-this.from}}function xl(t,e,n){return e=Math.max(0,Math.min(t.length,e)),[e,Math.max(e,Math.min(t.length,n))]}function xi(t,e,n=!0,r=!0){return NK(t,e,n,r)}function MK(t){return t>=56320&&t<57344}function DK(t){return t>=55296&&t<56320}function DR(t,e){let n=t.charCodeAt(e);if(!DK(n)||e+1==t.length)return n;let r=t.charCodeAt(e+1);return MK(r)?(n-55296<<10)+(r-56320)+65536:n}function LK(t){return t<=65535?String.fromCharCode(t):(t-=65536,String.fromCharCode((t>>10)+55296,(t&1023)+56320))}function LR(t){return t<65536?1:2}const Eb=/\r\n?|\n/;var Wr=(function(t){return t[t.Simple=0]="Simple",t[t.TrackDel=1]="TrackDel",t[t.TrackBefore=2]="TrackBefore",t[t.TrackAfter=3]="TrackAfter",t})(Wr||(Wr={}));class bs{constructor(e){this.sections=e}get length(){let e=0;for(let n=0;ne)return s+(e-i);s+=l}else{if(r!=Wr.Simple&&d>=e&&(r==Wr.TrackDel&&ie||r==Wr.TrackBefore&&ie))return null;if(d>e||d==e&&n<0&&!l)return e==i||n<0?s:s+c;s+=c}i=d}if(e>i)throw new RangeError(`Position ${e} is out of range for changeset of length ${i}`);return s}touchesRange(e,n=e){for(let r=0,i=0;r=0&&i<=n&&l>=e)return in?"cover":!0;i=l}return!1}toString(){let e="";for(let n=0;n=0?":"+i:"")}return e}toJSON(){return this.sections}static fromJSON(e){if(!Array.isArray(e)||e.length%2||e.some(n=>typeof n!="number"))throw new RangeError("Invalid JSON representation of ChangeDesc");return new bs(e)}static create(e){return new bs(e)}}class $n extends bs{constructor(e,n){super(e),this.inserted=n}apply(e){if(this.length!=e.length)throw new RangeError("Applying change set to a document with the wrong length");return yb(this,(n,r,i,s,o)=>e=e.replace(i,i+(r-n),o),!1),e}mapDesc(e,n=!1){return xb(this,e,n,!0)}invert(e){let n=this.sections.slice(),r=[];for(let i=0,s=0;i=0){n[i]=l,n[i+1]=o;let c=i>>1;for(;r.length0&&io(r,n,s.text),s.forward(f),l+=f}let d=e[o++];for(;l>1].toJSON()))}return e}static of(e,n,r){let i=[],s=[],o=0,l=null;function c(f=!1){if(!f&&!i.length)return;om||p<0||m>n)throw new RangeError(`Invalid change range ${p} to ${m} (in doc of length ${n})`);let x=g?typeof g=="string"?Lt.of(g.split(r||Eb)):g:Lt.empty,v=x.length;if(p==m&&v==0)return;po&&rr(i,p-o,-1),rr(i,m-p,v),io(s,i,x),o=m}}return d(e),c(!l),l}static empty(e){return new $n(e?[e,-1]:[],[])}static fromJSON(e){if(!Array.isArray(e))throw new RangeError("Invalid JSON representation of ChangeSet");let n=[],r=[];for(let i=0;il&&typeof o!="string"))throw new RangeError("Invalid JSON representation of ChangeSet");if(s.length==1)n.push(s[0],0);else{for(;r.length=0&&n<=0&&n==t[i+1]?t[i]+=e:i>=0&&e==0&&t[i]==0?t[i+1]+=n:r?(t[i]+=e,t[i+1]+=n):t.push(e,n)}function io(t,e,n){if(n.length==0)return;let r=e.length-2>>1;if(r>1])),!(n||o==t.sections.length||t.sections[o+1]<0);)l=t.sections[o++],c=t.sections[o++];e(i,d,s,f,p),i=d,s=f}}}function xb(t,e,n,r=!1){let i=[],s=r?[]:null,o=new Ec(t),l=new Ec(e);for(let c=-1;;){if(o.done&&l.len||l.done&&o.len)throw new Error("Mismatched change set lengths");if(o.ins==-1&&l.ins==-1){let d=Math.min(o.len,l.len);rr(i,d,-1),o.forward(d),l.forward(d)}else if(l.ins>=0&&(o.ins<0||c==o.i||o.off==0&&(l.len=0&&c=0){let d=0,f=o.len;for(;f;)if(l.ins==-1){let p=Math.min(f,l.len);d+=p,f-=p,l.forward(p)}else if(l.ins==0&&l.lenc||o.ins>=0&&o.len>c)&&(l||r.length>d),s.forward2(c),o.forward(c)}}}}class Ec{constructor(e){this.set=e,this.i=0,this.next()}next(){let{sections:e}=this.set;this.i>1;return n>=e.length?Lt.empty:e[n]}textBit(e){let{inserted:n}=this.set,r=this.i-2>>1;return r>=n.length&&!e?Lt.empty:n[r].slice(this.off,e==null?void 0:this.off+e)}forward(e){e==this.len?this.next():(this.len-=e,this.off+=e)}forward2(e){this.ins==-1?this.forward(e):e==this.ins?this.next():(this.ins-=e,this.off+=e)}}class zo{constructor(e,n,r){this.from=e,this.to=n,this.flags=r}get anchor(){return this.flags&32?this.to:this.from}get head(){return this.flags&32?this.from:this.to}get empty(){return this.from==this.to}get assoc(){return this.flags&8?-1:this.flags&16?1:0}get bidiLevel(){let e=this.flags&7;return e==7?null:e}get goalColumn(){let e=this.flags>>6;return e==16777215?void 0:e}map(e,n=-1){let r,i;return this.empty?r=i=e.mapPos(this.from,n):(r=e.mapPos(this.from,1),i=e.mapPos(this.to,-1)),r==this.from&&i==this.to?this:new zo(r,i,this.flags)}extend(e,n=e){if(e<=this.anchor&&n>=this.anchor)return Pe.range(e,n);let r=Math.abs(e-this.anchor)>Math.abs(n-this.anchor)?e:n;return Pe.range(this.anchor,r)}eq(e,n=!1){return this.anchor==e.anchor&&this.head==e.head&&(!n||!this.empty||this.assoc==e.assoc)}toJSON(){return{anchor:this.anchor,head:this.head}}static fromJSON(e){if(!e||typeof e.anchor!="number"||typeof e.head!="number")throw new RangeError("Invalid JSON representation for SelectionRange");return Pe.range(e.anchor,e.head)}static create(e,n,r){return new zo(e,n,r)}}class Pe{constructor(e,n){this.ranges=e,this.mainIndex=n}map(e,n=-1){return e.empty?this:Pe.create(this.ranges.map(r=>r.map(e,n)),this.mainIndex)}eq(e,n=!1){if(this.ranges.length!=e.ranges.length||this.mainIndex!=e.mainIndex)return!1;for(let r=0;re.toJSON()),main:this.mainIndex}}static fromJSON(e){if(!e||!Array.isArray(e.ranges)||typeof e.main!="number"||e.main>=e.ranges.length)throw new RangeError("Invalid JSON representation for EditorSelection");return new Pe(e.ranges.map(n=>zo.fromJSON(n)),e.main)}static single(e,n=e){return new Pe([Pe.range(e,n)],0)}static create(e,n=0){if(e.length==0)throw new RangeError("A selection needs at least one range");for(let r=0,i=0;ie?8:0)|s)}static normalized(e,n=0){let r=e[n];e.sort((i,s)=>i.from-s.from),n=e.indexOf(r);for(let i=1;is.head?Pe.range(c,l):Pe.range(l,c))}}return new Pe(e,n)}}function FR(t,e){for(let n of t.ranges)if(n.to>e)throw new RangeError("Selection points outside of document")}let x1=0;class tt{constructor(e,n,r,i,s){this.combine=e,this.compareInput=n,this.compare=r,this.isStatic=i,this.id=x1++,this.default=e([]),this.extensions=typeof s=="function"?s(this):s}get reader(){return this}static define(e={}){return new tt(e.combine||(n=>n),e.compareInput||((n,r)=>n===r),e.compare||(e.combine?(n,r)=>n===r:v1),!!e.static,e.enables)}of(e){return new $f([],this,0,e)}compute(e,n){if(this.isStatic)throw new Error("Can't compute a static facet");return new $f(e,this,1,n)}computeN(e,n){if(this.isStatic)throw new Error("Can't compute a static facet");return new $f(e,this,2,n)}from(e,n){return n||(n=r=>r),this.compute([e],r=>n(r.field(e)))}}function v1(t,e){return t==e||t.length==e.length&&t.every((n,r)=>n===e[r])}class $f{constructor(e,n,r,i){this.dependencies=e,this.facet=n,this.type=r,this.value=i,this.id=x1++}dynamicSlot(e){var n;let r=this.value,i=this.facet.compareInput,s=this.id,o=e[s]>>1,l=this.type==2,c=!1,d=!1,f=[];for(let p of this.dependencies)p=="doc"?c=!0:p=="selection"?d=!0:(((n=e[p.id])!==null&&n!==void 0?n:1)&1)==0&&f.push(e[p.id]);return{create(p){return p.values[o]=r(p),1},update(p,m){if(c&&m.docChanged||d&&(m.docChanged||m.selection)||vb(p,f)){let g=r(p);if(l?!eS(g,p.values[o],i):!i(g,p.values[o]))return p.values[o]=g,1}return 0},reconfigure:(p,m)=>{let g,x=m.config.address[s];if(x!=null){let v=vh(m,x);if(this.dependencies.every(S=>S instanceof tt?m.facet(S)===p.facet(S):S instanceof bo?m.field(S,!1)==p.field(S,!1):!0)||(l?eS(g=r(p),v,i):i(g=r(p),v)))return p.values[o]=v,0}else g=r(p);return p.values[o]=g,1}}}}function eS(t,e,n){if(t.length!=e.length)return!1;for(let r=0;rt[c.id]),i=n.map(c=>c.type),s=r.filter(c=>!(c&1)),o=t[e.id]>>1;function l(c){let d=[];for(let f=0;fr===i),e);return e.provide&&(n.provides=e.provide(n)),n}create(e){let n=e.facet(xf).find(r=>r.field==this);return(n?.create||this.createF)(e)}slot(e){let n=e[this.id]>>1;return{create:r=>(r.values[n]=this.create(r),1),update:(r,i)=>{let s=r.values[n],o=this.updateF(s,i);return this.compareF(s,o)?0:(r.values[n]=o,1)},reconfigure:(r,i)=>{let s=r.facet(xf),o=i.facet(xf),l;return(l=s.find(c=>c.field==this))&&l!=o.find(c=>c.field==this)?(r.values[n]=l.create(r),1):i.config.address[this.id]!=null?(r.values[n]=i.field(this),0):(r.values[n]=this.create(r),1)}}}init(e){return[this,xf.of({field:this,create:e})]}get extension(){return this}}const Uo={lowest:4,low:3,default:2,high:1,highest:0};function Lu(t){return e=>new BR(e,t)}const w1={highest:Lu(Uo.highest),high:Lu(Uo.high),default:Lu(Uo.default),low:Lu(Uo.low),lowest:Lu(Uo.lowest)};class BR{constructor(e,n){this.inner=e,this.prec=n}}class Sp{of(e){return new wb(this,e)}reconfigure(e){return Sp.reconfigure.of({compartment:this,extension:e})}get(e){return e.config.compartments.get(this)}}class wb{constructor(e,n){this.compartment=e,this.inner=n}}class xh{constructor(e,n,r,i,s,o){for(this.base=e,this.compartments=n,this.dynamicSlots=r,this.address=i,this.staticValues=s,this.facets=o,this.statusTemplate=[];this.statusTemplate.length>1]}static resolve(e,n,r){let i=[],s=Object.create(null),o=new Map;for(let m of FK(e,n,o))m instanceof bo?i.push(m):(s[m.facet.id]||(s[m.facet.id]=[])).push(m);let l=Object.create(null),c=[],d=[];for(let m of i)l[m.id]=d.length<<1,d.push(g=>m.slot(g));let f=r?.config.facets;for(let m in s){let g=s[m],x=g[0].facet,v=f&&f[m]||[];if(g.every(S=>S.type==0))if(l[x.id]=c.length<<1|1,v1(v,g))c.push(r.facet(x));else{let S=x.combine(g.map(C=>C.value));c.push(r&&x.compare(S,r.facet(x))?r.facet(x):S)}else{for(let S of g)S.type==0?(l[S.id]=c.length<<1|1,c.push(S.value)):(l[S.id]=d.length<<1,d.push(C=>S.dynamicSlot(C)));l[x.id]=d.length<<1,d.push(S=>PK(S,x,g))}}let p=d.map(m=>m(l));return new xh(e,o,p,l,c,s)}}function FK(t,e,n){let r=[[],[],[],[],[]],i=new Map;function s(o,l){let c=i.get(o);if(c!=null){if(c<=l)return;let d=r[c].indexOf(o);d>-1&&r[c].splice(d,1),o instanceof wb&&n.delete(o.compartment)}if(i.set(o,l),Array.isArray(o))for(let d of o)s(d,l);else if(o instanceof wb){if(n.has(o.compartment))throw new RangeError("Duplicate use of compartment in extensions");let d=e.get(o.compartment)||o.inner;n.set(o.compartment,d),s(d,l)}else if(o instanceof BR)s(o.inner,o.prec);else if(o instanceof bo)r[l].push(o),o.provides&&s(o.provides,l);else if(o instanceof $f)r[l].push(o),o.facet.extensions&&s(o.facet.extensions,Uo.default);else{let d=o.extension;if(!d)throw new Error(`Unrecognized extension value in extension set (${o}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);s(d,l)}}return s(t,Uo.default),r.reduce((o,l)=>o.concat(l))}function tc(t,e){if(e&1)return 2;let n=e>>1,r=t.status[n];if(r==4)throw new Error("Cyclic dependency between fields and/or facets");if(r&2)return r;t.status[n]=4;let i=t.computeSlot(t,t.config.dynamicSlots[n]);return t.status[n]=2|i}function vh(t,e){return e&1?t.config.staticValues[e>>1]:t.values[e>>1]}const UR=tt.define(),Tb=tt.define({combine:t=>t.some(e=>e),static:!0}),HR=tt.define({combine:t=>t.length?t[0]:void 0,static:!0}),zR=tt.define(),jR=tt.define(),$R=tt.define(),WR=tt.define({combine:t=>t.length?t[0]:!1});class Hl{constructor(e,n){this.type=e,this.value=n}static define(){return new BK}}class BK{of(e){return new Hl(this,e)}}class UK{constructor(e){this.map=e}of(e){return new bn(this,e)}}class bn{constructor(e,n){this.type=e,this.value=n}map(e){let n=this.type.map(this.value,e);return n===void 0?void 0:n==this.value?this:new bn(this.type,n)}is(e){return this.type==e}static define(e={}){return new UK(e.map||(n=>n))}static mapEffects(e,n){if(!e.length)return e;let r=[];for(let i of e){let s=i.map(n);s&&r.push(s)}return r}}bn.reconfigure=bn.define();bn.appendConfig=bn.define();class or{constructor(e,n,r,i,s,o){this.startState=e,this.changes=n,this.selection=r,this.effects=i,this.annotations=s,this.scrollIntoView=o,this._doc=null,this._state=null,r&&FR(r,n.newLength),s.some(l=>l.type==or.time)||(this.annotations=s.concat(or.time.of(Date.now())))}static create(e,n,r,i,s,o){return new or(e,n,r,i,s,o)}get newDoc(){return this._doc||(this._doc=this.changes.apply(this.startState.doc))}get newSelection(){return this.selection||this.startState.selection.map(this.changes)}get state(){return this._state||this.startState.applyTransaction(this),this._state}annotation(e){for(let n of this.annotations)if(n.type==e)return n.value}get docChanged(){return!this.changes.empty}get reconfigured(){return this.startState.config!=this.state.config}isUserEvent(e){let n=this.annotation(or.userEvent);return!!(n&&(n==e||n.length>e.length&&n.slice(0,e.length)==e&&n[e.length]=="."))}}or.time=Hl.define();or.userEvent=Hl.define();or.addToHistory=Hl.define();or.remote=Hl.define();function HK(t,e){let n=[];for(let r=0,i=0;;){let s,o;if(r=t[r]))s=t[r++],o=t[r++];else if(i=0;i--){let s=r[i](t);s instanceof or?t=s:Array.isArray(s)&&s.length==1&&s[0]instanceof or?t=s[0]:t=GR(e,ul(s),!1)}return t}function jK(t){let e=t.startState,n=e.facet($R),r=t;for(let i=n.length-1;i>=0;i--){let s=n[i](t);s&&Object.keys(s).length&&(r=VR(r,Sb(e,s,t.changes.newLength),!0))}return r==t?t:or.create(e,t.changes,t.selection,r.effects,r.annotations,r.scrollIntoView)}const $K=[];function ul(t){return t==null?$K:Array.isArray(t)?t:[t]}var Cn=(function(t){return t[t.Word=0]="Word",t[t.Space=1]="Space",t[t.Other=2]="Other",t})(Cn||(Cn={}));const WK=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;let _b;try{_b=new RegExp("[\\p{Alphabetic}\\p{Number}_]","u")}catch{}function VK(t){if(_b)return _b.test(t);for(let e=0;e"€"&&(n.toUpperCase()!=n.toLowerCase()||WK.test(n)))return!0}return!1}function GK(t){return e=>{if(!/\S/.test(e))return Cn.Space;if(VK(e))return Cn.Word;for(let n=0;n-1)return Cn.Word;return Cn.Other}}class Wt{constructor(e,n,r,i,s,o){this.config=e,this.doc=n,this.selection=r,this.values=i,this.status=e.statusTemplate.slice(),this.computeSlot=s,o&&(o._state=this);for(let l=0;li.set(d,c)),n=null),i.set(l.value.compartment,l.value.extension)):l.is(bn.reconfigure)?(n=null,r=l.value):l.is(bn.appendConfig)&&(n=null,r=ul(r).concat(l.value));let s;n?s=e.startState.values.slice():(n=xh.resolve(r,i,this),s=new Wt(n,this.doc,this.selection,n.dynamicSlots.map(()=>null),(c,d)=>d.reconfigure(c,this),null).values);let o=e.startState.facet(Tb)?e.newSelection:e.newSelection.asSingle();new Wt(n,e.newDoc,o,s,(l,c)=>c.update(l,e),e)}replaceSelection(e){return typeof e=="string"&&(e=this.toText(e)),this.changeByRange(n=>({changes:{from:n.from,to:n.to,insert:e},range:Pe.cursor(n.from+e.length)}))}changeByRange(e){let n=this.selection,r=e(n.ranges[0]),i=this.changes(r.changes),s=[r.range],o=ul(r.effects);for(let l=1;lo.spec.fromJSON(l,c)))}}return Wt.create({doc:e.doc,selection:Pe.fromJSON(e.selection),extensions:n.extensions?i.concat([n.extensions]):i})}static create(e={}){let n=xh.resolve(e.extensions||[],new Map),r=e.doc instanceof Lt?e.doc:Lt.of((e.doc||"").split(n.staticFacet(Wt.lineSeparator)||Eb)),i=e.selection?e.selection instanceof Pe?e.selection:Pe.single(e.selection.anchor,e.selection.head):Pe.single(0);return FR(i,r.length),n.staticFacet(Tb)||(i=i.asSingle()),new Wt(n,r,i,n.dynamicSlots.map(()=>null),(s,o)=>o.create(s),null)}get tabSize(){return this.facet(Wt.tabSize)}get lineBreak(){return this.facet(Wt.lineSeparator)||` `}get readOnly(){return this.facet(WR)}phrase(e,...n){for(let r of this.facet(Wt.phrases))if(Object.prototype.hasOwnProperty.call(r,e)){e=r[e];break}return n.length&&(e=e.replace(/\$(\$|\d*)/g,(r,i)=>{if(i=="$")return"$";let s=+(i||1);return!s||s>n.length?r:n[s-1]})),e}languageDataAt(e,n,r=-1){let i=[];for(let s of this.facet(UR))for(let o of s(this,n,r))Object.prototype.hasOwnProperty.call(o,e)&&i.push(o[e]);return i}charCategorizer(e){return GK(this.languageDataAt("wordChars",e).join(""))}wordAt(e){let{text:n,from:r,length:i}=this.doc.lineAt(e),s=this.charCategorizer(e),o=e-r,l=e-r;for(;o>0;){let c=xi(n,o,!1);if(s(n.slice(c,o))!=Cn.Word)break;o=c}for(;lt.length?t[0]:4});Wt.lineSeparator=HR;Wt.readOnly=WR;Wt.phrases=tt.define({compare(t,e){let n=Object.keys(t),r=Object.keys(e);return n.length==r.length&&n.every(i=>t[i]==e[i])}});Wt.languageData=UR;Wt.changeFilter=zR;Wt.transactionFilter=jR;Wt.transactionExtender=$R;Sp.reconfigure=bn.define();function T1(t,e,n={}){let r={};for(let i of t)for(let s of Object.keys(i)){let o=i[s],l=r[s];if(l===void 0)r[s]=o;else if(!(l===o||o===void 0))if(Object.hasOwnProperty.call(n,s))r[s]=n[s](l,o);else throw new Error("Config merge conflict for field "+s)}for(let i in e)r[i]===void 0&&(r[i]=e[i]);return r}class vl{eq(e){return this==e}range(e,n=e){return yc.create(e,n,this)}}vl.prototype.startSide=vl.prototype.endSide=0;vl.prototype.point=!1;vl.prototype.mapMode=Wr.TrackDel;class yc{constructor(e,n,r){this.from=e,this.to=n,this.value=r}static create(e,n,r){return new yc(e,n,r)}}function Cb(t,e){return t.from-e.from||t.value.startSide-e.value.startSide}class S1{constructor(e,n,r,i){this.from=e,this.to=n,this.value=r,this.maxPoint=i}get length(){return this.to[this.to.length-1]}findIndex(e,n,r,i=0){let s=r?this.to:this.from;for(let o=i,l=s.length;;){if(o==l)return o;let c=o+l>>1,d=s[c]-e||(r?this.value[c].endSide:this.value[c].startSide)-n;if(c==o)return d>=0?o:l;d>=0?l=c:o=c+1}}between(e,n,r,i){for(let s=this.findIndex(n,-1e9,!0),o=this.findIndex(r,1e9,!1,s);sg||m==g&&d.startSide>0&&d.endSide<=0)continue;(g-m||d.endSide-d.startSide)<0||(o<0&&(o=m),d.point&&(l=Math.max(l,g-m)),r.push(d),i.push(m-o),s.push(g-o))}return{mapped:r.length?new S1(i,s,r,l):null,pos:o}}}class Ht{constructor(e,n,r,i){this.chunkPos=e,this.chunk=n,this.nextLayer=r,this.maxPoint=i}static create(e,n,r,i){return new Ht(e,n,r,i)}get length(){let e=this.chunk.length-1;return e<0?0:Math.max(this.chunkEnd(e),this.nextLayer.length)}get size(){if(this.isEmpty)return 0;let e=this.nextLayer.size;for(let n of this.chunk)e+=n.value.length;return e}chunkEnd(e){return this.chunkPos[e]+this.chunk[e].length}update(e){let{add:n=[],sort:r=!1,filterFrom:i=0,filterTo:s=this.length}=e,o=e.filter;if(n.length==0&&!o)return this;if(r&&(n=n.slice().sort(Cb)),this.isEmpty)return n.length?Ht.of(n):this;let l=new KR(this,null,-1).goto(0),c=0,d=[],f=new xc;for(;l.value||c=0){let p=n[c++];f.addInner(p.from,p.to,p.value)||d.push(p)}else l.rangeIndex==1&&l.chunkIndexthis.chunkEnd(l.chunkIndex)||sl.to||s=s&&e<=s+o.length&&o.between(s,e-s,n-s,r)===!1)return}this.nextLayer.between(e,n,r)}}iter(e=0){return vc.from([this]).goto(e)}get isEmpty(){return this.nextLayer==this}static iter(e,n=0){return vc.from(e).goto(n)}static compare(e,n,r,i,s=-1){let o=e.filter(p=>p.maxPoint>0||!p.isEmpty&&p.maxPoint>=s),l=n.filter(p=>p.maxPoint>0||!p.isEmpty&&p.maxPoint>=s),c=tS(o,l,r),d=new Pu(o,c,s),f=new Pu(l,c,s);r.iterGaps((p,m,g)=>nS(d,p,f,m,g,i)),r.empty&&r.length==0&&nS(d,0,f,0,0,i)}static eq(e,n,r=0,i){i==null&&(i=999999999);let s=e.filter(f=>!f.isEmpty&&n.indexOf(f)<0),o=n.filter(f=>!f.isEmpty&&e.indexOf(f)<0);if(s.length!=o.length)return!1;if(!s.length)return!0;let l=tS(s,o),c=new Pu(s,l,0).goto(r),d=new Pu(o,l,0).goto(r);for(;;){if(c.to!=d.to||!Ab(c.active,d.active)||c.point&&(!d.point||!c.point.eq(d.point)))return!1;if(c.to>i)return!0;c.next(),d.next()}}static spans(e,n,r,i,s=-1){let o=new Pu(e,null,s).goto(n),l=n,c=o.openStart;for(;;){let d=Math.min(o.to,r);if(o.point){let f=o.activeForPoint(o.to),p=o.pointFroml&&(i.span(l,d,o.active,c),c=o.openEnd(d));if(o.to>r)return c+(o.point&&o.to>r?1:0);l=o.to,o.next()}}static of(e,n=!1){let r=new xc;for(let i of e instanceof yc?[e]:n?KK(e):e)r.add(i.from,i.to,i.value);return r.finish()}static join(e){if(!e.length)return Ht.empty;let n=e[e.length-1];for(let r=e.length-2;r>=0;r--)for(let i=e[r];i!=Ht.empty;i=i.nextLayer)n=new Ht(i.chunkPos,i.chunk,n,Math.max(i.maxPoint,n.maxPoint));return n}}Ht.empty=new Ht([],[],null,-1);function KK(t){if(t.length>1)for(let e=t[0],n=1;n0)return t.slice().sort(Cb);e=r}return t}Ht.empty.nextLayer=Ht.empty;class xc{finishChunk(e){this.chunks.push(new S1(this.from,this.to,this.value,this.maxPoint)),this.chunkPos.push(this.chunkStart),this.chunkStart=-1,this.setMaxPoint=Math.max(this.setMaxPoint,this.maxPoint),this.maxPoint=-1,e&&(this.from=[],this.to=[],this.value=[])}constructor(){this.chunks=[],this.chunkPos=[],this.chunkStart=-1,this.last=null,this.lastFrom=-1e9,this.lastTo=-1e9,this.from=[],this.to=[],this.value=[],this.maxPoint=-1,this.setMaxPoint=-1,this.nextLayer=null}add(e,n,r){this.addInner(e,n,r)||(this.nextLayer||(this.nextLayer=new xc)).add(e,n,r)}addInner(e,n,r){let i=e-this.lastTo||r.startSide-this.last.endSide;if(i<=0&&(e-this.lastFrom||r.startSide-this.last.startSide)<0)throw new Error("Ranges must be added sorted by `from` position and `startSide`");return i<0?!1:(this.from.length==250&&this.finishChunk(!0),this.chunkStart<0&&(this.chunkStart=e),this.from.push(e-this.chunkStart),this.to.push(n-this.chunkStart),this.last=r,this.lastFrom=e,this.lastTo=n,this.value.push(r),r.point&&(this.maxPoint=Math.max(this.maxPoint,n-e)),!0)}addChunk(e,n){if((e-this.lastTo||n.value[0].startSide-this.last.endSide)<0)return!1;this.from.length&&this.finishChunk(!0),this.setMaxPoint=Math.max(this.setMaxPoint,n.maxPoint),this.chunks.push(n),this.chunkPos.push(e);let r=n.value.length-1;return this.last=n.value[r],this.lastFrom=n.from[r]+e,this.lastTo=n.to[r]+e,!0}finish(){return this.finishInner(Ht.empty)}finishInner(e){if(this.from.length&&this.finishChunk(!1),this.chunks.length==0)return e;let n=Ht.create(this.chunkPos,this.chunks,this.nextLayer?this.nextLayer.finishInner(e):e,this.setMaxPoint);return this.from=null,n}}function tS(t,e,n){let r=new Map;for(let s of t)for(let o=0;o=this.minPoint)break}}setRangeIndex(e){if(e==this.layer.chunk[this.chunkIndex].value.length){if(this.chunkIndex++,this.skip)for(;this.chunkIndex=r&&i.push(new KR(o,n,r,s));return i.length==1?i[0]:new vc(i)}get startSide(){return this.value?this.value.startSide:0}goto(e,n=-1e9){for(let r of this.heap)r.goto(e,n);for(let r=this.heap.length>>1;r>=0;r--)h0(this.heap,r);return this.next(),this}forward(e,n){for(let r of this.heap)r.forward(e,n);for(let r=this.heap.length>>1;r>=0;r--)h0(this.heap,r);(this.to-e||this.value.endSide-n)<0&&this.next()}next(){if(this.heap.length==0)this.from=this.to=1e9,this.value=null,this.rank=-1;else{let e=this.heap[0];this.from=e.from,this.to=e.to,this.value=e.value,this.rank=e.rank,e.value&&e.next(),h0(this.heap,0)}}}function h0(t,e){for(let n=t[e];;){let r=(e<<1)+1;if(r>=t.length)break;let i=t[r];if(r+1=0&&(i=t[r+1],r++),n.compare(i)<0)break;t[r]=n,t[e]=i,e=r}}class Pu{constructor(e,n,r){this.minPoint=r,this.active=[],this.activeTo=[],this.activeRank=[],this.minActive=-1,this.point=null,this.pointFrom=0,this.pointRank=0,this.to=-1e9,this.endSide=0,this.openStart=-1,this.cursor=vc.from(e,n,r)}goto(e,n=-1e9){return this.cursor.goto(e,n),this.active.length=this.activeTo.length=this.activeRank.length=0,this.minActive=-1,this.to=e,this.endSide=n,this.openStart=-1,this.next(),this}forward(e,n){for(;this.minActive>-1&&(this.activeTo[this.minActive]-e||this.active[this.minActive].endSide-n)<0;)this.removeActive(this.minActive);this.cursor.forward(e,n)}removeActive(e){vf(this.active,e),vf(this.activeTo,e),vf(this.activeRank,e),this.minActive=rS(this.active,this.activeTo)}addActive(e){let n=0,{value:r,to:i,rank:s}=this.cursor;for(;n0;)n++;wf(this.active,n,r),wf(this.activeTo,n,i),wf(this.activeRank,n,s),e&&wf(e,n,this.cursor.from),this.minActive=rS(this.active,this.activeTo)}next(){let e=this.to,n=this.point;this.point=null;let r=this.openStart<0?[]:null;for(;;){let i=this.minActive;if(i>-1&&(this.activeTo[i]-this.cursor.from||this.active[i].endSide-this.cursor.startSide)<0){if(this.activeTo[i]>e){this.to=this.activeTo[i],this.endSide=this.active[i].endSide;break}this.removeActive(i),r&&vf(r,i)}else if(this.cursor.value)if(this.cursor.from>e){this.to=this.cursor.from,this.endSide=this.cursor.startSide;break}else{let s=this.cursor.value;if(!s.point)this.addActive(r),this.cursor.next();else if(n&&this.cursor.to==this.to&&this.cursor.from=0&&r[i]=0&&!(this.activeRank[r]e||this.activeTo[r]==e&&this.active[r].endSide>=this.point.endSide)&&n.push(this.active[r]);return n.reverse()}openEnd(e){let n=0;for(let r=this.activeTo.length-1;r>=0&&this.activeTo[r]>e;r--)n++;return n}}function nS(t,e,n,r,i,s){t.goto(e),n.goto(r);let o=r+i,l=r,c=r-e;for(;;){let d=t.to+c-n.to,f=d||t.endSide-n.endSide,p=f<0?t.to+c:n.to,m=Math.min(p,o);if(t.point||n.point?t.point&&n.point&&(t.point==n.point||t.point.eq(n.point))&&Ab(t.activeForPoint(t.to),n.activeForPoint(n.to))||s.comparePoint(l,m,t.point,n.point):m>l&&!Ab(t.active,n.active)&&s.compareRange(l,m,t.active,n.active),p>o)break;(d||t.openEnd!=n.openEnd)&&s.boundChange&&s.boundChange(p),l=p,f<=0&&t.next(),f>=0&&n.next()}}function Ab(t,e){if(t.length!=e.length)return!1;for(let n=0;n=e;r--)t[r+1]=t[r];t[e]=n}function rS(t,e){let n=-1,r=1e9;for(let i=0;i=e)return i;if(i==t.length)break;s+=t.charCodeAt(i)==9?n-s%n:1,i=xi(t,i)}return t.length}const kb="ͼ",iS=typeof Symbol>"u"?"__"+kb:Symbol.for(kb),Nb=typeof Symbol>"u"?"__styleSet"+Math.floor(Math.random()*1e8):Symbol("styleSet"),sS=typeof globalThis<"u"?globalThis:typeof window<"u"?window:{};class wl{constructor(e,n){this.rules=[];let{finish:r}=n||{};function i(o){return/^@/.test(o)?[o]:o.split(/,\s*/)}function s(o,l,c,d){let f=[],p=/^@(\w+)\b/.exec(o[0]),m=p&&p[1]=="keyframes";if(p&&l==null)return c.push(o[0]+";");for(let g in l){let x=l[g];if(/&/.test(g))s(g.split(/,\s*/).map(v=>o.map(S=>v.replace(/&/,S))).reduce((v,S)=>v.concat(S)),x,c);else if(x&&typeof x=="object"){if(!p)throw new RangeError("The value of a property ("+g+") should be a primitive value.");s(i(g),x,f,m)}else x!=null&&f.push(g.replace(/_.*/,"").replace(/[A-Z]/g,v=>"-"+v.toLowerCase())+": "+x+";")}(f.length||m)&&c.push((r&&!p&&!d?o.map(r):o).join(", ")+" {"+f.join(" ")+"}")}for(let o in e)s(i(o),e[o],this.rules)}getRules(){return this.rules.join(` `)}static newName(){let e=sS[iS]||1;return sS[iS]=e+1,kb+e.toString(36)}static mount(e,n,r){let i=e[Nb],s=r&&r.nonce;i?s&&i.setNonce(s):i=new qK(e,s),i.mount(Array.isArray(n)?n:[n],e)}}let oS=new Map;class qK{constructor(e,n){let r=e.ownerDocument||e,i=r.defaultView;if(!e.head&&e.adoptedStyleSheets&&i.CSSStyleSheet){let s=oS.get(r);if(s)return e[Nb]=s;this.sheet=new i.CSSStyleSheet,oS.set(r,this)}else this.styleTag=r.createElement("style"),n&&this.styleTag.setAttribute("nonce",n);this.modules=[],e[Nb]=this}mount(e,n){let r=this.sheet,i=0,s=0;for(let o=0;o-1&&(this.modules.splice(c,1),s--,c=-1),c==-1){if(this.modules.splice(s++,0,l),r)for(let d=0;d",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},XK=typeof navigator<"u"&&/Mac/.test(navigator.platform),QK=typeof navigator<"u"&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);for(var qn=0;qn<10;qn++)fo[48+qn]=fo[96+qn]=String(qn);for(var qn=1;qn<=24;qn++)fo[qn+111]="F"+qn;for(var qn=65;qn<=90;qn++)fo[qn]=String.fromCharCode(qn+32),wc[qn]=String.fromCharCode(qn);for(var p0 in fo)wc.hasOwnProperty(p0)||(wc[p0]=fo[p0]);function ZK(t){var e=XK&&t.metaKey&&t.shiftKey&&!t.ctrlKey&&!t.altKey||QK&&t.shiftKey&&t.key&&t.key.length==1||t.key=="Unidentified",n=!e&&t.key||(t.shiftKey?wc:fo)[t.keyCode]||t.key||"Unidentified";return n=="Esc"&&(n="Escape"),n=="Del"&&(n="Delete"),n=="Left"&&(n="ArrowLeft"),n=="Up"&&(n="ArrowUp"),n=="Right"&&(n="ArrowRight"),n=="Down"&&(n="ArrowDown"),n}function Kn(){var t=arguments[0];typeof t=="string"&&(t=document.createElement(t));var e=1,n=arguments[1];if(n&&typeof n=="object"&&n.nodeType==null&&!Array.isArray(n)){for(var r in n)if(Object.prototype.hasOwnProperty.call(n,r)){var i=n[r];typeof i=="string"?t.setAttribute(r,i):i!=null&&(t[r]=i)}e++}for(;e.995&&n<1.005||!isFinite(n)||Math.abs(e.width-t.offsetWidth)<1)&&(n=1),(r>.995&&r<1.005||!isFinite(r)||Math.abs(e.height-t.offsetHeight)<1)&&(r=1),{scaleX:n,scaleY:r}}function eY(t,e,n,r,i,s,o,l){let c=t.ownerDocument,d=c.defaultView||window;for(let f=t,p=!1;f&&!p;)if(f.nodeType==1){let m,g=f==c.body,x=1,v=1;if(g)m=JK(d);else{if(/^(fixed|sticky)$/.test(getComputedStyle(f).position)&&(p=!0),f.scrollHeight<=f.clientHeight&&f.scrollWidth<=f.clientWidth){f=f.assignedSlot||f.parentNode;continue}let A=f.getBoundingClientRect();({scaleX:x,scaleY:v}=qR(f,A)),m={left:A.left,right:A.left+f.clientWidth*x,top:A.top,bottom:A.top+f.clientHeight*v}}let S=0,C=0;if(i=="nearest")e.top0&&e.bottom>m.bottom+C&&(C=e.bottom-m.bottom+o)):e.bottom>m.bottom&&(C=e.bottom-m.bottom+o,n<0&&e.top-C0&&e.right>m.right+S&&(S=e.right-m.right+s)):e.right>m.right&&(S=e.right-m.right+s,n<0&&e.leftm.bottom||e.leftm.right)&&(e={left:Math.max(e.left,m.left),right:Math.min(e.right,m.right),top:Math.max(e.top,m.top),bottom:Math.min(e.bottom,m.bottom)}),f=f.assignedSlot||f.parentNode}else if(f.nodeType==11)f=f.host;else break}function tY(t){let e=t.ownerDocument,n,r;for(let i=t.parentNode;i&&!(i==e.body||n&&r);)if(i.nodeType==1)!r&&i.scrollHeight>i.clientHeight&&(r=i),!n&&i.scrollWidth>i.clientWidth&&(n=i),i=i.assignedSlot||i.parentNode;else if(i.nodeType==11)i=i.host;else break;return{x:n,y:r}}class nY{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}eq(e){return this.anchorNode==e.anchorNode&&this.anchorOffset==e.anchorOffset&&this.focusNode==e.focusNode&&this.focusOffset==e.focusOffset}setRange(e){let{anchorNode:n,focusNode:r}=e;this.set(n,Math.min(e.anchorOffset,n?Xi(n):0),r,Math.min(e.focusOffset,r?Xi(r):0))}set(e,n,r,i){this.anchorNode=e,this.anchorOffset=n,this.focusNode=r,this.focusOffset=i}}let Wa=null;function XR(t){if(t.setActive)return t.setActive();if(Wa)return t.focus(Wa);let e=[];for(let n=t;n&&(e.push(n,n.scrollTop,n.scrollLeft),n!=n.ownerDocument);n=n.parentNode);if(t.focus(Wa==null?{get preventScroll(){return Wa={preventScroll:!0},!0}}:void 0),!Wa){Wa=!1;for(let n=0;nMath.max(1,t.scrollHeight-t.clientHeight-4)}function JR(t,e){for(let n=t,r=e;;){if(n.nodeType==3&&r>0)return{node:n,offset:r};if(n.nodeType==1&&r>0){if(n.contentEditable=="false")return null;n=n.childNodes[r-1],r=Xi(n)}else if(n.parentNode&&!wh(n))r=ea(n),n=n.parentNode;else return null}}function eI(t,e){for(let n=t,r=e;;){if(n.nodeType==3&&rn)return p.domBoundsAround(e,n,d);if(m>=e&&i==-1&&(i=c,s=d),d>n&&p.dom.parentNode==this.dom){o=c,l=f;break}f=m,d=m+p.breakAfter}return{from:s,to:l<0?r+this.length:l,startDOM:(i?this.children[i-1].dom.nextSibling:null)||this.dom.firstChild,endDOM:o=0?this.children[o].dom:null}}markDirty(e=!1){this.flags|=2,this.markParentsDirty(e)}markParentsDirty(e){for(let n=this.parent;n;n=n.parent){if(e&&(n.flags|=2),n.flags&1)return;n.flags|=1,e=!1}}setParent(e){this.parent!=e&&(this.parent=e,this.flags&7&&this.markParentsDirty(!0))}setDOM(e){this.dom!=e&&(this.dom&&(this.dom.cmView=null),this.dom=e,e.cmView=this)}get rootView(){for(let e=this;;){let n=e.parent;if(!n)return e;e=n}}replaceChildren(e,n,r=_1){this.markDirty();for(let i=e;ithis.pos||e==this.pos&&(n>0||this.i==0||this.children[this.i-1].breakAfter))return this.off=e-this.pos,this;let r=this.children[--this.i];this.pos-=r.length+r.breakAfter}}}function nI(t,e,n,r,i,s,o,l,c){let{children:d}=t,f=d.length?d[e]:null,p=s.length?s[s.length-1]:null,m=p?p.breakAfter:o;if(!(e==r&&f&&!o&&!m&&s.length<2&&f.merge(n,i,s.length?p:null,n==0,l,c))){if(r0&&(!o&&s.length&&f.merge(n,f.length,s[0],!1,l,0)?f.breakAfter=s.shift().breakAfter:(n2);var Fe={mac:dS||/Mac/.test(Or.platform),windows:/Win/.test(Or.platform),linux:/Linux|X11/.test(Or.platform),ie:Cp,ie_version:iI?Ib.documentMode||6:Mb?+Mb[1]:Ob?+Ob[1]:0,gecko:cS,gecko_version:cS?+(/Firefox\/(\d+)/.exec(Or.userAgent)||[0,0])[1]:0,chrome:!!m0,chrome_version:m0?+m0[1]:0,ios:dS,android:/Android\b/.test(Or.userAgent),safari:sI,webkit_version:sY?+(/\bAppleWebKit\/(\d+)/.exec(Or.userAgent)||[0,0])[1]:0,tabSize:Ib.documentElement.style.tabSize!=null?"tab-size":"-moz-tab-size"};const oY=256;class _i extends Gt{constructor(e){super(),this.text=e}get length(){return this.text.length}createDOM(e){this.setDOM(e||document.createTextNode(this.text))}sync(e,n){this.dom||this.createDOM(),this.dom.nodeValue!=this.text&&(n&&n.node==this.dom&&(n.written=!0),this.dom.nodeValue=this.text)}reuseDOM(e){e.nodeType==3&&this.createDOM(e)}merge(e,n,r){return this.flags&8||r&&(!(r instanceof _i)||this.length-(n-e)+r.length>oY||r.flags&8)?!1:(this.text=this.text.slice(0,e)+(r?r.text:"")+this.text.slice(n),this.markDirty(),!0)}split(e){let n=new _i(this.text.slice(e));return this.text=this.text.slice(0,e),this.markDirty(),n.flags|=this.flags&8,n}localPosFromDOM(e,n){return e==this.dom?n:n?this.text.length:0}domAtPos(e){return new ir(this.dom,e)}domBoundsAround(e,n,r){return{from:r,to:r+this.length,startDOM:this.dom,endDOM:this.dom.nextSibling}}coordsAt(e,n){return aY(this.dom,e,n)}}class Ts extends Gt{constructor(e,n=[],r=0){super(),this.mark=e,this.children=n,this.length=r;for(let i of n)i.setParent(this)}setAttrs(e){if(QR(e),this.mark.class&&(e.className=this.mark.class),this.mark.attrs)for(let n in this.mark.attrs)e.setAttribute(n,this.mark.attrs[n]);return e}canReuseDOM(e){return super.canReuseDOM(e)&&!((this.flags|e.flags)&8)}reuseDOM(e){e.nodeName==this.mark.tagName.toUpperCase()&&(this.setDOM(e),this.flags|=6)}sync(e,n){this.dom?this.flags&4&&this.setAttrs(this.dom):this.setDOM(this.setAttrs(document.createElement(this.mark.tagName))),super.sync(e,n)}merge(e,n,r,i,s,o){return r&&(!(r instanceof Ts&&r.mark.eq(this.mark))||e&&s<=0||ne&&n.push(r=e&&(i=s),r=c,s++}let o=this.length-e;return this.length=e,i>-1&&(this.children.length=i,this.markDirty()),new Ts(this.mark,n,o)}domAtPos(e){return oI(this,e)}coordsAt(e,n){return lI(this,e,n)}}function aY(t,e,n){let r=t.nodeValue.length;e>r&&(e=r);let i=e,s=e,o=0;e==0&&n<0||e==r&&n>=0?Fe.chrome||Fe.gecko||(e?(i--,o=1):s=0)?0:l.length-1];return Fe.safari&&!o&&c.width==0&&(c=Array.prototype.find.call(l,d=>d.width)||c),o?_p(c,o<0):c||null}class jo extends Gt{static create(e,n,r){return new jo(e,n,r)}constructor(e,n,r){super(),this.widget=e,this.length=n,this.side=r,this.prevWidget=null}split(e){let n=jo.create(this.widget,this.length-e,this.side);return this.length-=e,n}sync(e){(!this.dom||!this.widget.updateDOM(this.dom,e))&&(this.dom&&this.prevWidget&&this.prevWidget.destroy(this.dom),this.prevWidget=null,this.setDOM(this.widget.toDOM(e)),this.widget.editable||(this.dom.contentEditable="false"))}getSide(){return this.side}merge(e,n,r,i,s,o){return r&&(!(r instanceof jo)||!this.widget.compare(r.widget)||e>0&&s<=0||n0)?ir.before(this.dom):ir.after(this.dom,e==this.length)}domBoundsAround(){return null}coordsAt(e,n){let r=this.widget.coordsAt(this.dom,e,n);if(r)return r;let i=this.dom.getClientRects(),s=null;if(!i.length)return null;let o=this.side?this.side<0:e>0;for(let l=o?i.length-1:0;s=i[l],!(e>0?l==0:l==i.length-1||s.top0?ir.before(this.dom):ir.after(this.dom)}localPosFromDOM(){return 0}domBoundsAround(){return null}coordsAt(e){return this.dom.getBoundingClientRect()}get overrideDOMText(){return Lt.empty}get isHidden(){return!0}}_i.prototype.children=jo.prototype.children=Tl.prototype.children=_1;function oI(t,e){let n=t.dom,{children:r}=t,i=0;for(let s=0;is&&e0;s--){let o=r[s-1];if(o.dom.parentNode==n)return o.domAtPos(o.length)}for(let s=i;s0&&e instanceof Ts&&i.length&&(r=i[i.length-1])instanceof Ts&&r.mark.eq(e.mark)?aI(r,e.children[0],n-1):(i.push(e),e.setParent(t)),t.length+=e.length}function lI(t,e,n){let r=null,i=-1,s=null,o=-1;function l(d,f){for(let p=0,m=0;p=f&&(g.children.length?l(g,f-m):(!s||s.isHidden&&(n>0||uY(s,g)))&&(x>f||m==x&&g.getSide()>0)?(s=g,o=f-m):(m-1?1:0)!=i.length-(n&&i.indexOf(n)>-1?1:0))return!1;for(let s of r)if(s!=n&&(i.indexOf(s)==-1||t[s]!==e[s]))return!1;return!0}function Lb(t,e,n){let r=!1;if(e)for(let i in e)n&&i in n||(r=!0,i=="style"?t.style.cssText="":t.removeAttribute(i));if(n)for(let i in n)e&&e[i]==n[i]||(r=!0,i=="style"?t.style.cssText=n[i]:t.setAttribute(i,n[i]));return r}function cY(t){let e=Object.create(null);for(let n=0;n0?3e8:-4e8:n>0?1e8:-1e8,new ho(e,n,n,r,e.widget||null,!1)}static replace(e){let n=!!e.block,r,i;if(e.isBlockGap)r=-5e8,i=4e8;else{let{start:s,end:o}=uI(e,n);r=(s?n?-3e8:-1:5e8)-1,i=(o?n?2e8:1:-6e8)+1}return new ho(e,r,i,n,e.widget||null,!0)}static line(e){return new jc(e)}static set(e,n=!1){return Ht.of(e,n)}hasHeight(){return this.widget?this.widget.estimatedHeight>-1:!1}}en.none=Ht.empty;class zc extends en{constructor(e){let{start:n,end:r}=uI(e);super(n?-1:5e8,r?1:-6e8,null,e),this.tagName=e.tagName||"span",this.class=e.class||"",this.attrs=e.attributes||null}eq(e){var n,r;return this==e||e instanceof zc&&this.tagName==e.tagName&&(this.class||((n=this.attrs)===null||n===void 0?void 0:n.class))==(e.class||((r=e.attrs)===null||r===void 0?void 0:r.class))&&Th(this.attrs,e.attrs,"class")}range(e,n=e){if(e>=n)throw new RangeError("Mark decorations may not be empty");return super.range(e,n)}}zc.prototype.point=!1;class jc extends en{constructor(e){super(-2e8,-2e8,null,e)}eq(e){return e instanceof jc&&this.spec.class==e.spec.class&&Th(this.spec.attributes,e.spec.attributes)}range(e,n=e){if(n!=e)throw new RangeError("Line decoration ranges must be zero-length");return super.range(e,n)}}jc.prototype.mapMode=Wr.TrackBefore;jc.prototype.point=!0;class ho extends en{constructor(e,n,r,i,s,o){super(n,r,s,e),this.block=i,this.isReplace=o,this.mapMode=i?n<=0?Wr.TrackBefore:Wr.TrackAfter:Wr.TrackDel}get type(){return this.startSide!=this.endSide?ii.WidgetRange:this.startSide<=0?ii.WidgetBefore:ii.WidgetAfter}get heightRelevant(){return this.block||!!this.widget&&(this.widget.estimatedHeight>=5||this.widget.lineBreaks>0)}eq(e){return e instanceof ho&&dY(this.widget,e.widget)&&this.block==e.block&&this.startSide==e.startSide&&this.endSide==e.endSide}range(e,n=e){if(this.isReplace&&(e>n||e==n&&this.startSide>0&&this.endSide<=0))throw new RangeError("Invalid range for replacement decoration");if(!this.isReplace&&n!=e)throw new RangeError("Widget decorations can only have zero-length ranges");return super.range(e,n)}}ho.prototype.point=!0;function uI(t,e=!1){let{inclusiveStart:n,inclusiveEnd:r}=t;return n==null&&(n=t.inclusive),r==null&&(r=t.inclusive),{start:n??e,end:r??e}}function dY(t,e){return t==e||!!(t&&e&&t.compare(e))}function Vf(t,e,n,r=0){let i=n.length-1;i>=0&&n[i]+r>=t?n[i]=Math.max(n[i],e):n.push(t,e)}class In extends Gt{constructor(){super(...arguments),this.children=[],this.length=0,this.prevAttrs=void 0,this.attrs=null,this.breakAfter=0}merge(e,n,r,i,s,o){if(r){if(!(r instanceof In))return!1;this.dom||r.transferDOM(this)}return i&&this.setDeco(r?r.attrs:null),rI(this,e,n,r?r.children.slice():[],s,o),!0}split(e){let n=new In;if(n.breakAfter=this.breakAfter,this.length==0)return n;let{i:r,off:i}=this.childPos(e);i&&(n.append(this.children[r].split(i),0),this.children[r].merge(i,this.children[r].length,null,!1,0,0),r++);for(let s=r;s0&&this.children[r-1].length==0;)this.children[--r].destroy();return this.children.length=r,this.markDirty(),this.length=e,n}transferDOM(e){this.dom&&(this.markDirty(),e.setDOM(this.dom),e.prevAttrs=this.prevAttrs===void 0?this.attrs:this.prevAttrs,this.prevAttrs=void 0,this.dom=null)}setDeco(e){Th(this.attrs,e)||(this.dom&&(this.prevAttrs=this.attrs,this.markDirty()),this.attrs=e)}append(e,n){aI(this,e,n)}addLineDeco(e){let n=e.spec.attributes,r=e.spec.class;n&&(this.attrs=Db(n,this.attrs||{})),r&&(this.attrs=Db({class:r},this.attrs||{}))}domAtPos(e){return oI(this,e)}reuseDOM(e){e.nodeName=="DIV"&&(this.setDOM(e),this.flags|=6)}sync(e,n){var r;this.dom?this.flags&4&&(QR(this.dom),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0):(this.setDOM(document.createElement("div")),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0),this.prevAttrs!==void 0&&(Lb(this.dom,this.prevAttrs,this.attrs),this.dom.classList.add("cm-line"),this.prevAttrs=void 0),super.sync(e,n);let i=this.dom.lastChild;for(;i&&Gt.get(i)instanceof Ts;)i=i.lastChild;if(!i||!this.length||i.nodeName!="BR"&&((r=Gt.get(i))===null||r===void 0?void 0:r.isEditable)==!1&&(!Fe.ios||!this.children.some(s=>s instanceof _i))){let s=document.createElement("BR");s.cmIgnore=!0,this.dom.appendChild(s)}}measureTextSize(){if(this.children.length==0||this.length>20)return null;let e=0,n;for(let r of this.children){if(!(r instanceof _i)||/[^ -~]/.test(r.text))return null;let i=Sc(r.dom);if(i.length!=1)return null;e+=i[0].width,n=i[0].height}return e?{lineHeight:this.dom.getBoundingClientRect().height,charWidth:e/this.length,textHeight:n}:null}coordsAt(e,n){let r=lI(this,e,n);if(!this.children.length&&r&&this.parent){let{heightOracle:i}=this.parent.view.viewState,s=r.bottom-r.top;if(Math.abs(s-i.lineHeight)<2&&i.textHeight=n){if(s instanceof In)return s;if(o>n)break}i=o+s.breakAfter}return null}}class Es extends Gt{constructor(e,n,r){super(),this.widget=e,this.length=n,this.deco=r,this.breakAfter=0,this.prevWidget=null}merge(e,n,r,i,s,o){return r&&(!(r instanceof Es)||!this.widget.compare(r.widget)||e>0&&s<=0||n0}}class Pb extends C1{constructor(e){super(),this.height=e}toDOM(){let e=document.createElement("div");return e.className="cm-gap",this.updateDOM(e),e}eq(e){return e.height==this.height}updateDOM(e){return e.style.height=this.height+"px",!0}get editable(){return!0}get estimatedHeight(){return this.height}ignoreEvent(){return!1}}class rc{constructor(e,n,r,i){this.doc=e,this.pos=n,this.end=r,this.disallowBlockEffectsFor=i,this.content=[],this.curLine=null,this.breakAtStart=0,this.pendingBuffer=0,this.bufferMarks=[],this.atCursorPos=!0,this.openStart=-1,this.openEnd=-1,this.text="",this.textOff=0,this.cursor=e.iter(),this.skip=n}posCovered(){if(this.content.length==0)return!this.breakAtStart&&this.doc.lineAt(this.pos).from!=this.pos;let e=this.content[this.content.length-1];return!(e.breakAfter||e instanceof Es&&e.deco.endSide<0)}getLine(){return this.curLine||(this.content.push(this.curLine=new In),this.atCursorPos=!0),this.curLine}flushBuffer(e=this.bufferMarks){this.pendingBuffer&&(this.curLine.append(Tf(new Tl(-1),e),e.length),this.pendingBuffer=0)}addBlockWidget(e){this.flushBuffer(),this.curLine=null,this.content.push(e)}finish(e){this.pendingBuffer&&e<=this.bufferMarks.length?this.flushBuffer():this.pendingBuffer=0,!this.posCovered()&&!(e&&this.content.length&&this.content[this.content.length-1]instanceof Es)&&this.getLine()}buildText(e,n,r){for(;e>0;){if(this.textOff==this.text.length){let{value:s,lineBreak:o,done:l}=this.cursor.next(this.skip);if(this.skip=0,l)throw new Error("Ran out of text content when drawing inline views");if(o){this.posCovered()||this.getLine(),this.content.length?this.content[this.content.length-1].breakAfter=1:this.breakAtStart=1,this.flushBuffer(),this.curLine=null,this.atCursorPos=!0,e--;continue}else this.text=s,this.textOff=0}let i=Math.min(this.text.length-this.textOff,e,512);this.flushBuffer(n.slice(n.length-r)),this.getLine().append(Tf(new _i(this.text.slice(this.textOff,this.textOff+i)),n),r),this.atCursorPos=!0,this.textOff+=i,e-=i,r=0}}span(e,n,r,i){this.buildText(n-e,r,i),this.pos=n,this.openStart<0&&(this.openStart=i)}point(e,n,r,i,s,o){if(this.disallowBlockEffectsFor[o]&&r instanceof ho){if(r.block)throw new RangeError("Block decorations may not be specified via plugins");if(n>this.doc.lineAt(this.pos).to)throw new RangeError("Decorations that replace line breaks may not be specified via plugins")}let l=n-e;if(r instanceof ho)if(r.block)r.startSide>0&&!this.posCovered()&&this.getLine(),this.addBlockWidget(new Es(r.widget||Sl.block,l,r));else{let c=jo.create(r.widget||Sl.inline,l,l?0:r.startSide),d=this.atCursorPos&&!c.isEditable&&s<=i.length&&(e0),f=!c.isEditable&&(ei.length||r.startSide<=0),p=this.getLine();this.pendingBuffer==2&&!d&&!c.isEditable&&(this.pendingBuffer=0),this.flushBuffer(i),d&&(p.append(Tf(new Tl(1),i),s),s=i.length+Math.max(0,s-i.length)),p.append(Tf(c,i),s),this.atCursorPos=f,this.pendingBuffer=f?ei.length?1:2:0,this.pendingBuffer&&(this.bufferMarks=i.slice())}else this.doc.lineAt(this.pos).from==this.pos&&this.getLine().addLineDeco(r);l&&(this.textOff+l<=this.text.length?this.textOff+=l:(this.skip+=l-(this.text.length-this.textOff),this.text="",this.textOff=0),this.pos=n),this.openStart<0&&(this.openStart=s)}static build(e,n,r,i,s){let o=new rc(e,n,r,s);return o.openEnd=Ht.spans(i,n,r,o),o.openStart<0&&(o.openStart=o.openEnd),o.finish(o.openEnd),o}}function Tf(t,e){for(let n of e)t=new Ts(n,[t],t.length);return t}class Sl extends C1{constructor(e){super(),this.tag=e}eq(e){return e.tag==this.tag}toDOM(){return document.createElement(this.tag)}updateDOM(e){return e.nodeName.toLowerCase()==this.tag}get isHidden(){return!0}}Sl.inline=new Sl("span");Sl.block=new Sl("div");var ar=(function(t){return t[t.LTR=0]="LTR",t[t.RTL=1]="RTL",t})(ar||(ar={}));const na=ar.LTR,A1=ar.RTL;function cI(t){let e=[];for(let n=0;n=n){if(l.level==r)return o;(s<0||(i!=0?i<0?l.fromn:e[s].level>l.level))&&(s=o)}}if(s<0)throw new RangeError("Index out of range");return s}}function fI(t,e){if(t.length!=e.length)return!1;for(let n=0;n=0;v-=3)if(Fi[v+1]==-g){let S=Fi[v+2],C=S&2?i:S&4?S&1?s:i:0;C&&(Vt[p]=Vt[Fi[v]]=C),l=v;break}}else{if(Fi.length==189)break;Fi[l++]=p,Fi[l++]=m,Fi[l++]=c}else if((x=Vt[p])==2||x==1){let v=x==i;c=v?0:1;for(let S=l-3;S>=0;S-=3){let C=Fi[S+2];if(C&2)break;if(v)Fi[S+2]|=2;else{if(C&4)break;Fi[S+2]|=4}}}}}function bY(t,e,n,r){for(let i=0,s=r;i<=n.length;i++){let o=i?n[i-1].to:t,l=ic;)x==S&&(x=n[--v].from,S=v?n[v-1].to:t),Vt[--x]=g;c=f}else s=d,c++}}}function Bb(t,e,n,r,i,s,o){let l=r%2?2:1;if(r%2==i%2)for(let c=e,d=0;cc&&o.push(new so(c,v.from,g));let S=v.direction==na!=!(g%2);Ub(t,S?r+1:r,i,v.inner,v.from,v.to,o),c=v.to}x=v.to}else{if(x==n||(f?Vt[x]!=l:Vt[x]==l))break;x++}m?Bb(t,c,x,r+1,i,m,o):ce;){let f=!0,p=!1;if(!d||c>s[d-1].to){let v=Vt[c-1];v!=l&&(f=!1,p=v==16)}let m=!f&&l==1?[]:null,g=f?r:r+1,x=c;e:for(;;)if(d&&x==s[d-1].to){if(p)break e;let v=s[--d];if(!f)for(let S=v.from,C=d;;){if(S==e)break e;if(C&&s[C-1].to==S)S=s[--C].from;else{if(Vt[S-1]==l)break e;break}}if(m)m.push(v);else{v.toVt.length;)Vt[Vt.length]=256;let r=[],i=e==na?0:1;return Ub(t,i,i,n,0,t.length,r),r}function hI(t){return[new so(0,t,0)]}let pI="";function yY(t,e,n,r,i){var s;let o=r.head-t.from,l=so.find(e,o,(s=r.bidiLevel)!==null&&s!==void 0?s:-1,r.assoc),c=e[l],d=c.side(i,n);if(o==d){let m=l+=i?1:-1;if(m<0||m>=e.length)return null;c=e[l=m],o=c.side(!i,n),d=c.side(i,n)}let f=xi(t.text,o,c.forward(i,n));(fc.to)&&(f=d),pI=t.text.slice(Math.min(o,f),Math.max(o,f));let p=l==(i?e.length-1:0)?null:e[l+(i?1:-1)];return p&&f==d&&p.level+(i?0:1)t.some(e=>e)}),vY=tt.define({combine:t=>t.some(e=>e)}),wI=tt.define();class dl{constructor(e,n="nearest",r="nearest",i=5,s=5,o=!1){this.range=e,this.y=n,this.x=r,this.yMargin=i,this.xMargin=s,this.isSnapshot=o}map(e){return e.empty?this:new dl(this.range.map(e),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}clip(e){return this.range.to<=e.doc.length?this:new dl(Pe.cursor(e.doc.length),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}}const Sf=bn.define({map:(t,e)=>t.map(e)}),TI=bn.define();function gs(t,e,n){let r=t.facet(EI);r.length?r[0](e):window.onerror&&window.onerror(String(e),n,void 0,void 0,e)||(n?console.error(n+":",e):console.error(e))}const ms=tt.define({combine:t=>t.length?t[0]:!0});let wY=0;const el=tt.define({combine(t){return t.filter((e,n)=>{for(let r=0;r{let c=[];return o&&c.push(_c.of(d=>{let f=d.plugin(l);return f?o(f):en.none})),s&&c.push(s(l)),c})}static fromClass(e,n){return Ss.define((r,i)=>new e(r,i),n)}}class g0{constructor(e){this.spec=e,this.mustUpdate=null,this.value=null}get plugin(){return this.spec&&this.spec.plugin}update(e){if(this.value){if(this.mustUpdate){let n=this.mustUpdate;if(this.mustUpdate=null,this.value.update)try{this.value.update(n)}catch(r){if(gs(n.state,r,"CodeMirror plugin crashed"),this.value.destroy)try{this.value.destroy()}catch{}this.deactivate()}}}else if(this.spec)try{this.value=this.spec.plugin.create(e,this.spec.arg)}catch(n){gs(e.state,n,"CodeMirror plugin crashed"),this.deactivate()}return this}destroy(e){var n;if(!((n=this.value)===null||n===void 0)&&n.destroy)try{this.value.destroy()}catch(r){gs(e.state,r,"CodeMirror plugin crashed")}}deactivate(){this.spec=this.value=null}}const SI=tt.define(),R1=tt.define(),_c=tt.define(),_I=tt.define(),I1=tt.define(),CI=tt.define();function hS(t,e){let n=t.state.facet(CI);if(!n.length)return n;let r=n.map(s=>s instanceof Function?s(t):s),i=[];return Ht.spans(r,e.from,e.to,{point(){},span(s,o,l,c){let d=s-e.from,f=o-e.from,p=i;for(let m=l.length-1;m>=0;m--,c--){let g=l[m].spec.bidiIsolate,x;if(g==null&&(g=xY(e.text,d,f)),c>0&&p.length&&(x=p[p.length-1]).to==d&&x.direction==g)x.to=f,p=x.inner;else{let v={from:d,to:f,direction:g,inner:[]};p.push(v),p=v.inner}}}}),i}const AI=tt.define();function kI(t){let e=0,n=0,r=0,i=0;for(let s of t.state.facet(AI)){let o=s(t);o&&(o.left!=null&&(e=Math.max(e,o.left)),o.right!=null&&(n=Math.max(n,o.right)),o.top!=null&&(r=Math.max(r,o.top)),o.bottom!=null&&(i=Math.max(i,o.bottom)))}return{left:e,right:n,top:r,bottom:i}}const $u=tt.define();class si{constructor(e,n,r,i){this.fromA=e,this.toA=n,this.fromB=r,this.toB=i}join(e){return new si(Math.min(this.fromA,e.fromA),Math.max(this.toA,e.toA),Math.min(this.fromB,e.fromB),Math.max(this.toB,e.toB))}addToSet(e){let n=e.length,r=this;for(;n>0;n--){let i=e[n-1];if(!(i.fromA>r.toA)){if(i.toAf)break;s+=2}if(!c)return r;new si(c.fromA,c.toA,c.fromB,c.toB).addToSet(r),o=c.toA,l=c.toB}}}class Sh{constructor(e,n,r){this.view=e,this.state=n,this.transactions=r,this.flags=0,this.startState=e.state,this.changes=$n.empty(this.startState.doc.length);for(let s of r)this.changes=this.changes.compose(s.changes);let i=[];this.changes.iterChangedRanges((s,o,l,c)=>i.push(new si(s,o,l,c))),this.changedRanges=i}static create(e,n,r){return new Sh(e,n,r)}get viewportChanged(){return(this.flags&4)>0}get viewportMoved(){return(this.flags&8)>0}get heightChanged(){return(this.flags&2)>0}get geometryChanged(){return this.docChanged||(this.flags&18)>0}get focusChanged(){return(this.flags&1)>0}get docChanged(){return!this.changes.empty}get selectionSet(){return this.transactions.some(e=>e.selection)}get empty(){return this.flags==0&&this.transactions.length==0}}class pS extends Gt{get length(){return this.view.state.doc.length}constructor(e){super(),this.view=e,this.decorations=[],this.dynamicDecorationMap=[!1],this.domChanged=null,this.hasComposition=null,this.markedForComposition=new Set,this.editContextFormatting=en.none,this.lastCompositionAfterCursor=!1,this.minWidth=0,this.minWidthFrom=0,this.minWidthTo=0,this.impreciseAnchor=null,this.impreciseHead=null,this.forceSelection=!1,this.lastUpdate=Date.now(),this.setDOM(e.contentDOM),this.children=[new In],this.children[0].setParent(this),this.updateDeco(),this.updateInner([new si(0,0,0,e.state.doc.length)],0,null)}update(e){var n;let r=e.changedRanges;this.minWidth>0&&r.length&&(r.every(({fromA:d,toA:f})=>fthis.minWidthTo)?(this.minWidthFrom=e.changes.mapPos(this.minWidthFrom,1),this.minWidthTo=e.changes.mapPos(this.minWidthTo,1)):this.minWidth=this.minWidthFrom=this.minWidthTo=0),this.updateEditContextFormatting(e);let i=-1;this.view.inputState.composing>=0&&!this.view.observer.editContext&&(!((n=this.domChanged)===null||n===void 0)&&n.newSel?i=this.domChanged.newSel.head:!NY(e.changes,this.hasComposition)&&!e.selectionSet&&(i=e.state.selection.main.head));let s=i>-1?SY(this.view,e.changes,i):null;if(this.domChanged=null,this.hasComposition){this.markedForComposition.clear();let{from:d,to:f}=this.hasComposition;r=new si(d,f,e.changes.mapPos(d,-1),e.changes.mapPos(f,1)).addToSet(r.slice())}this.hasComposition=s?{from:s.range.fromB,to:s.range.toB}:null,(Fe.ie||Fe.chrome)&&!s&&e&&e.state.doc.lines!=e.startState.doc.lines&&(this.forceSelection=!0);let o=this.decorations,l=this.updateDeco(),c=AY(o,l,e.changes);return r=si.extendWithRanges(r,c),!(this.flags&7)&&r.length==0?!1:(this.updateInner(r,e.startState.doc.length,s),e.transactions.length&&(this.lastUpdate=Date.now()),!0)}updateInner(e,n,r){this.view.viewState.mustMeasureContent=!0,this.updateChildren(e,n,r);let{observer:i}=this.view;i.ignore(()=>{this.dom.style.height=this.view.viewState.contentHeight/this.view.scaleY+"px",this.dom.style.flexBasis=this.minWidth?this.minWidth+"px":"";let o=Fe.chrome||Fe.ios?{node:i.selectionRange.focusNode,written:!1}:void 0;this.sync(this.view,o),this.flags&=-8,o&&(o.written||i.selectionRange.focusNode!=o.node)&&(this.forceSelection=!0),this.dom.style.height=""}),this.markedForComposition.forEach(o=>o.flags&=-9);let s=[];if(this.view.viewport.from||this.view.viewport.to=0?i[o]:null;if(!l)break;let{fromA:c,toA:d,fromB:f,toB:p}=l,m,g,x,v;if(r&&r.range.fromBf){let M=rc.build(this.view.state.doc,f,r.range.fromB,this.decorations,this.dynamicDecorationMap),F=rc.build(this.view.state.doc,r.range.toB,p,this.decorations,this.dynamicDecorationMap);g=M.breakAtStart,x=M.openStart,v=F.openEnd;let I=this.compositionView(r);F.breakAtStart?I.breakAfter=1:F.content.length&&I.merge(I.length,I.length,F.content[0],!1,F.openStart,0)&&(I.breakAfter=F.content[0].breakAfter,F.content.shift()),M.content.length&&I.merge(0,0,M.content[M.content.length-1],!0,0,M.openEnd)&&M.content.pop(),m=M.content.concat(I).concat(F.content)}else({content:m,breakAtStart:g,openStart:x,openEnd:v}=rc.build(this.view.state.doc,f,p,this.decorations,this.dynamicDecorationMap));let{i:S,off:C}=s.findPos(d,1),{i:A,off:k}=s.findPos(c,-1);nI(this,A,k,S,C,m,g,x,v)}r&&this.fixCompositionDOM(r)}updateEditContextFormatting(e){this.editContextFormatting=this.editContextFormatting.map(e.changes);for(let n of e.transactions)for(let r of n.effects)r.is(TI)&&(this.editContextFormatting=r.value)}compositionView(e){let n=new _i(e.text.nodeValue);n.flags|=8;for(let{deco:i}of e.marks)n=new Ts(i,[n],n.length);let r=new In;return r.append(n,0),r}fixCompositionDOM(e){let n=(s,o)=>{o.flags|=8|(o.children.some(c=>c.flags&7)?1:0),this.markedForComposition.add(o);let l=Gt.get(s);l&&l!=o&&(l.dom=null),o.setDOM(s)},r=this.childPos(e.range.fromB,1),i=this.children[r.i];n(e.line,i);for(let s=e.marks.length-1;s>=-1;s--)r=i.childPos(r.off,1),i=i.children[r.i],n(s>=0?e.marks[s].node:e.text,i)}updateSelection(e=!1,n=!1){(e||!this.view.observer.selectionRange.focusNode)&&this.view.observer.readSelectionRange();let r=this.view.root.activeElement,i=r==this.dom,s=!i&&!(this.view.state.facet(ms)||this.dom.tabIndex>-1)&&Wf(this.dom,this.view.observer.selectionRange)&&!(r&&this.dom.contains(r));if(!(i||n||s))return;let o=this.forceSelection;this.forceSelection=!1;let l=this.view.state.selection.main,c=this.moveToLine(this.domAtPos(l.anchor)),d=l.empty?c:this.moveToLine(this.domAtPos(l.head));if(Fe.gecko&&l.empty&&!this.hasComposition&&TY(c)){let p=document.createTextNode("");this.view.observer.ignore(()=>c.node.insertBefore(p,c.node.childNodes[c.offset]||null)),c=d=new ir(p,0),o=!0}let f=this.view.observer.selectionRange;(o||!f.focusNode||(!nc(c.node,c.offset,f.anchorNode,f.anchorOffset)||!nc(d.node,d.offset,f.focusNode,f.focusOffset))&&!this.suppressWidgetCursorChange(f,l))&&(this.view.observer.ignore(()=>{Fe.android&&Fe.chrome&&this.dom.contains(f.focusNode)&&kY(f.focusNode,this.dom)&&(this.dom.blur(),this.dom.focus({preventScroll:!0}));let p=Tc(this.view.root);if(p)if(l.empty){if(Fe.gecko){let m=_Y(c.node,c.offset);if(m&&m!=3){let g=(m==1?JR:eI)(c.node,c.offset);g&&(c=new ir(g.node,g.offset))}}p.collapse(c.node,c.offset),l.bidiLevel!=null&&p.caretBidiLevel!==void 0&&(p.caretBidiLevel=l.bidiLevel)}else if(p.extend){p.collapse(c.node,c.offset);try{p.extend(d.node,d.offset)}catch{}}else{let m=document.createRange();l.anchor>l.head&&([c,d]=[d,c]),m.setEnd(d.node,d.offset),m.setStart(c.node,c.offset),p.removeAllRanges(),p.addRange(m)}s&&this.view.root.activeElement==this.dom&&(this.dom.blur(),r&&r.focus())}),this.view.observer.setSelectionRange(c,d)),this.impreciseAnchor=c.precise?null:new ir(f.anchorNode,f.anchorOffset),this.impreciseHead=d.precise?null:new ir(f.focusNode,f.focusOffset)}suppressWidgetCursorChange(e,n){return this.hasComposition&&n.empty&&nc(e.focusNode,e.focusOffset,e.anchorNode,e.anchorOffset)&&this.posFromDOM(e.focusNode,e.focusOffset)==n.head}enforceCursorAssoc(){if(this.hasComposition)return;let{view:e}=this,n=e.state.selection.main,r=Tc(e.root),{anchorNode:i,anchorOffset:s}=e.observer.selectionRange;if(!r||!n.empty||!n.assoc||!r.modify)return;let o=In.find(this,n.head);if(!o)return;let l=o.posAtStart;if(n.head==l||n.head==l+o.length)return;let c=this.coordsAt(n.head,-1),d=this.coordsAt(n.head,1);if(!c||!d||c.bottom>d.top)return;let f=this.domAtPos(n.head+n.assoc);r.collapse(f.node,f.offset),r.modify("move",n.assoc<0?"forward":"backward","lineboundary"),e.observer.readSelectionRange();let p=e.observer.selectionRange;e.docView.posFromDOM(p.anchorNode,p.anchorOffset)!=n.from&&r.collapse(i,s)}moveToLine(e){let n=this.dom,r;if(e.node!=n)return e;for(let i=e.offset;!r&&i=0;i--){let s=Gt.get(n.childNodes[i]);s instanceof In&&(r=s.domAtPos(s.length))}return r?new ir(r.node,r.offset,!0):e}nearest(e){for(let n=e;n;){let r=Gt.get(n);if(r&&r.rootView==this)return r;n=n.parentNode}return null}posFromDOM(e,n){let r=this.nearest(e);if(!r)throw new RangeError("Trying to find position for a DOM position outside of the document");return r.localPosFromDOM(e,n)+r.posAtStart}domAtPos(e){let{i:n,off:r}=this.childCursor().findPos(e,-1);for(;n=0;o--){let l=this.children[o],c=s-l.breakAfter,d=c-l.length;if(ce||l.covers(1))&&(!r||l instanceof In&&!(r instanceof In&&n>=0)))r=l,i=d;else if(r&&d==e&&c==e&&l instanceof Es&&Math.abs(n)<2){if(l.deco.startSide<0)break;o&&(r=null)}s=d}return r?r.coordsAt(e-i,n):null}coordsForChar(e){let{i:n,off:r}=this.childPos(e,1),i=this.children[n];if(!(i instanceof In))return null;for(;i.children.length;){let{i:l,off:c}=i.childPos(r,1);for(;;l++){if(l==i.children.length)return null;if((i=i.children[l]).length)break}r=c}if(!(i instanceof _i))return null;let s=xi(i.text,r);if(s==r)return null;let o=ta(i.dom,r,s).getClientRects();for(let l=0;lMath.max(this.view.scrollDOM.clientWidth,this.minWidth)+1,l=-1,c=this.view.textDirection==ar.LTR;for(let d=0,f=0;fi)break;if(d>=r){let g=p.dom.getBoundingClientRect();if(n.push(g.height),o){let x=p.dom.lastChild,v=x?Sc(x):[];if(v.length){let S=v[v.length-1],C=c?S.right-g.left:g.right-S.left;C>l&&(l=C,this.minWidth=s,this.minWidthFrom=d,this.minWidthTo=m)}}}d=m+p.breakAfter}return n}textDirectionAt(e){let{i:n}=this.childPos(e,1);return getComputedStyle(this.children[n].dom).direction=="rtl"?ar.RTL:ar.LTR}measureTextSize(){for(let s of this.children)if(s instanceof In){let o=s.measureTextSize();if(o)return o}let e=document.createElement("div"),n,r,i;return e.className="cm-line",e.style.width="99999px",e.style.position="absolute",e.textContent="abc def ghi jkl mno pqr stu",this.view.observer.ignore(()=>{this.dom.appendChild(e);let s=Sc(e.firstChild)[0];n=e.getBoundingClientRect().height,r=s?s.width/27:7,i=s?s.height:n,e.remove()}),{lineHeight:n,charWidth:r,textHeight:i}}childCursor(e=this.length){let n=this.children.length;return n&&(e-=this.children[--n].length),new tI(this.children,e,n)}computeBlockGapDeco(){let e=[],n=this.view.viewState;for(let r=0,i=0;;i++){let s=i==n.viewports.length?null:n.viewports[i],o=s?s.from-1:this.length;if(o>r){let l=(n.lineBlockAt(o).bottom-n.lineBlockAt(r).top)/this.view.scaleY;e.push(en.replace({widget:new Pb(l),block:!0,inclusive:!0,isBlockGap:!0}).range(r,o))}if(!s)break;r=s.to+1}return en.set(e)}updateDeco(){let e=1,n=this.view.state.facet(_c).map(s=>(this.dynamicDecorationMap[e++]=typeof s=="function")?s(this.view):s),r=!1,i=this.view.state.facet(_I).map((s,o)=>{let l=typeof s=="function";return l&&(r=!0),l?s(this.view):s});for(i.length&&(this.dynamicDecorationMap[e++]=r,n.push(Ht.join(i))),this.decorations=[this.editContextFormatting,...n,this.computeBlockGapDeco(),this.view.viewState.lineGapDeco];en.anchor?-1:1),i;if(!r)return;!n.empty&&(i=this.coordsAt(n.anchor,n.anchor>n.head?-1:1))&&(r={left:Math.min(r.left,i.left),top:Math.min(r.top,i.top),right:Math.max(r.right,i.right),bottom:Math.max(r.bottom,i.bottom)});let s=kI(this.view),o={left:r.left-s.left,top:r.top-s.top,right:r.right+s.right,bottom:r.bottom+s.bottom},{offsetWidth:l,offsetHeight:c}=this.view.scrollDOM;eY(this.view.scrollDOM,o,n.head{re.from&&(n=!0)}),n}function RY(t,e,n=1){let r=t.charCategorizer(e),i=t.doc.lineAt(e),s=e-i.from;if(i.length==0)return Pe.cursor(e);s==0?n=1:s==i.length&&(n=-1);let o=s,l=s;n<0?o=xi(i.text,s,!1):l=xi(i.text,s);let c=r(i.text.slice(o,l));for(;o>0;){let d=xi(i.text,o,!1);if(r(i.text.slice(d,o))!=c)break;o=d}for(;lt?e.left-t:Math.max(0,t-e.right)}function OY(t,e){return e.top>t?e.top-t:Math.max(0,t-e.bottom)}function b0(t,e){return t.tope.top+1}function mS(t,e){return et.bottom?{top:t.top,left:t.left,right:t.right,bottom:e}:t}function zb(t,e,n){let r,i,s,o,l=!1,c,d,f,p;for(let x=t.firstChild;x;x=x.nextSibling){let v=Sc(x);for(let S=0;Sk||o==k&&s>A)&&(r=x,i=C,s=A,o=k,l=A?e0:SC.bottom&&(!f||f.bottomC.top)&&(d=x,p=C):f&&b0(f,C)?f=gS(f,C.bottom):p&&b0(p,C)&&(p=mS(p,C.top))}}if(f&&f.bottom>=n?(r=c,i=f):p&&p.top<=n&&(r=d,i=p),!r)return{node:t,offset:0};let m=Math.max(i.left,Math.min(i.right,e));if(r.nodeType==3)return bS(r,m,n);if(l&&r.contentEditable!="false")return zb(r,m,n);let g=Array.prototype.indexOf.call(t.childNodes,r)+(e>=(i.left+i.right)/2?1:0);return{node:t,offset:g}}function bS(t,e,n){let r=t.nodeValue.length,i=-1,s=1e9,o=0;for(let l=0;ln?f.top-n:n-f.bottom)-1;if(f.left-1<=e&&f.right+1>=e&&p=(f.left+f.right)/2,g=m;if((Fe.chrome||Fe.gecko)&&ta(t,l).getBoundingClientRect().left==f.right&&(g=!m),p<=0)return{node:t,offset:l+(g?1:0)};i=l+(g?1:0),s=p}}}return{node:t,offset:i>-1?i:o>0?t.nodeValue.length:0}}function RI(t,e,n,r=-1){var i,s;let o=t.contentDOM.getBoundingClientRect(),l=o.top+t.viewState.paddingTop,c,{docHeight:d}=t.viewState,{x:f,y:p}=e,m=p-l;if(m<0)return 0;if(m>d)return t.state.doc.length;for(let M=t.viewState.heightOracle.textHeight/2,F=!1;c=t.elementAtHeight(m),c.type!=ii.Text;)for(;m=r>0?c.bottom+M:c.top-M,!(m>=0&&m<=d);){if(F)return n?null:0;F=!0,r=-r}p=l+m;let g=c.from;if(gt.viewport.to)return t.viewport.to==t.state.doc.length?t.state.doc.length:n?null:ES(t,o,c,f,p);let x=t.dom.ownerDocument,v=t.root.elementFromPoint?t.root:x,S=v.elementFromPoint(f,p);S&&!t.contentDOM.contains(S)&&(S=null),S||(f=Math.max(o.left+1,Math.min(o.right-1,f)),S=v.elementFromPoint(f,p),S&&!t.contentDOM.contains(S)&&(S=null));let C,A=-1;if(S&&((i=t.docView.nearest(S))===null||i===void 0?void 0:i.isEditable)!=!1){if(x.caretPositionFromPoint){let M=x.caretPositionFromPoint(f,p);M&&({offsetNode:C,offset:A}=M)}else if(x.caretRangeFromPoint){let M=x.caretRangeFromPoint(f,p);M&&({startContainer:C,startOffset:A}=M,(!t.contentDOM.contains(C)||Fe.safari&&MY(C,A,f)||Fe.chrome&&DY(C,A,f))&&(C=void 0))}C&&(A=Math.min(Xi(C),A))}if(!C||!t.docView.dom.contains(C)){let M=In.find(t.docView,g);if(!M)return m>c.top+c.height/2?c.to:c.from;({node:C,offset:A}=zb(M.dom,f,p))}let k=t.docView.nearest(C);if(!k)return null;if(k.isWidget&&((s=k.dom)===null||s===void 0?void 0:s.nodeType)==1){let M=k.dom.getBoundingClientRect();return e.yt.defaultLineHeight*1.5){let l=t.viewState.heightOracle.textHeight,c=Math.floor((i-n.top-(t.defaultLineHeight-l)*.5)/l);s+=c*t.viewState.heightOracle.lineLength}let o=t.state.sliceDoc(n.from,n.to);return n.from+YK(o,s,t.state.tabSize)}function MY(t,e,n){let r,i=t;if(t.nodeType!=3||e!=(r=t.nodeValue.length))return!1;for(;;){let s=i.nextSibling;if(s){if(s.nodeName=="BR")break;return!1}else{let o=i.parentNode;if(!o||o.nodeName=="DIV")break;i=o}}return ta(t,r-1,r).getBoundingClientRect().right>n}function DY(t,e,n){if(e!=0)return!1;for(let i=t;;){let s=i.parentNode;if(!s||s.nodeType!=1||s.firstChild!=i)return!1;if(s.classList.contains("cm-line"))break;i=s}let r=t.nodeType==1?t.getBoundingClientRect():ta(t,0,Math.max(t.nodeValue.length,1)).getBoundingClientRect();return n-r.left>5}function LY(t,e,n){let r=t.lineBlockAt(e);if(Array.isArray(r.type)){let i;for(let s of r.type){if(s.from>e)break;if(!(s.toe)return s;(!i||s.type==ii.Text&&(i.type!=s.type||(n<0?s.frome)))&&(i=s)}}return i||r}return r}function PY(t,e,n,r){let i=LY(t,e.head,e.assoc||-1),s=!r||i.type!=ii.Text||!(t.lineWrapping||i.widgetLineBreaks)?null:t.coordsAtPos(e.assoc<0&&e.head>i.from?e.head-1:e.head);if(s){let o=t.dom.getBoundingClientRect(),l=t.textDirectionAt(i.from),c=t.posAtCoords({x:n==(l==ar.LTR)?o.right-1:o.left+1,y:(s.top+s.bottom)/2});if(c!=null)return Pe.cursor(c,n?-1:1)}return Pe.cursor(n?i.to:i.from,n?-1:1)}function yS(t,e,n,r){let i=t.state.doc.lineAt(e.head),s=t.bidiSpans(i),o=t.textDirectionAt(i.from);for(let l=e,c=null;;){let d=yY(i,s,o,l,n),f=pI;if(!d){if(i.number==(n?t.state.doc.lines:1))return l;f=` `,i=t.state.doc.line(i.number+(n?1:-1)),s=t.bidiSpans(i),d=t.visualLineSide(i,!n)}if(c){if(!c(f))return l}else{if(!r)return d;c=r(f)}l=d}}function FY(t,e,n){let r=t.state.charCategorizer(e),i=r(n);return s=>{let o=r(s);return i==Cn.Space&&(i=o),i==o}}function BY(t,e,n,r){let i=e.head,s=n?1:-1;if(i==(n?t.state.doc.length:0))return Pe.cursor(i,e.assoc);let o=e.goalColumn,l,c=t.contentDOM.getBoundingClientRect(),d=t.coordsAtPos(i,e.assoc||-1),f=t.documentTop;if(d)o==null&&(o=d.left-c.left),l=s<0?d.top:d.bottom;else{let g=t.viewState.lineBlockAt(i);o==null&&(o=Math.min(c.right-c.left,t.defaultCharacterWidth*(i-g.from))),l=(s<0?g.top:g.bottom)+f}let p=c.left+o,m=r??t.viewState.heightOracle.textHeight>>1;for(let g=0;;g+=10){let x=l+(m+g)*s,v=RI(t,{x:p,y:x},!1,s);if(xc.bottom||(s<0?vi)){let S=t.docView.coordsForChar(v),C=!S||x{if(e>s&&ei(t)),n.from,e.head>n.from?-1:1);return r==n.from?n:Pe.cursor(r,rs)&&this.lineBreak(),i=o}return this.findPointBefore(r,n),this}readTextNode(e){let n=e.nodeValue;for(let r of this.points)r.node==e&&(r.pos=this.text.length+Math.min(r.offset,n.length));for(let r=0,i=this.lineSeparator?null:/\r\n?|\n/g;;){let s=-1,o=1,l;if(this.lineSeparator?(s=n.indexOf(this.lineSeparator,r),o=this.lineSeparator.length):(l=i.exec(n))&&(s=l.index,o=l[0].length),this.append(n.slice(r,s<0?n.length:s)),s<0)break;if(this.lineBreak(),o>1)for(let c of this.points)c.node==e&&c.pos>this.text.length&&(c.pos-=o-1);r=s+o}}readNode(e){if(e.cmIgnore)return;let n=Gt.get(e),r=n&&n.overrideDOMText;if(r!=null){this.findPointInside(e,r.length);for(let i=r.iter();!i.next().done;)i.lineBreak?this.lineBreak():this.append(i.value)}else e.nodeType==3?this.readTextNode(e):e.nodeName=="BR"?e.nextSibling&&this.lineBreak():e.nodeType==1&&this.readRange(e.firstChild,null)}findPointBefore(e,n){for(let r of this.points)r.node==e&&e.childNodes[r.offset]==n&&(r.pos=this.text.length)}findPointInside(e,n){for(let r of this.points)(e.nodeType==3?r.node==e:e.contains(r.node))&&(r.pos=this.text.length+(HY(e,r.node,r.offset)?n:0))}}function HY(t,e,n){for(;;){if(!e||n-1;let{impreciseHead:s,impreciseAnchor:o}=e.docView;if(e.state.readOnly&&n>-1)this.newSel=null;else if(n>-1&&(this.bounds=e.docView.domBoundsAround(n,r,0))){let l=s||o?[]:WY(e),c=new UY(l,e.state);c.readRange(this.bounds.startDOM,this.bounds.endDOM),this.text=c.text,this.newSel=VY(l,this.bounds.from)}else{let l=e.observer.selectionRange,c=s&&s.node==l.focusNode&&s.offset==l.focusOffset||!Rb(e.contentDOM,l.focusNode)?e.state.selection.main.head:e.docView.posFromDOM(l.focusNode,l.focusOffset),d=o&&o.node==l.anchorNode&&o.offset==l.anchorOffset||!Rb(e.contentDOM,l.anchorNode)?e.state.selection.main.anchor:e.docView.posFromDOM(l.anchorNode,l.anchorOffset),f=e.viewport;if((Fe.ios||Fe.chrome)&&e.state.selection.main.empty&&c!=d&&(f.from>0||f.toDate.now()-100?t.inputState.lastKeyCode:-1;if(e.bounds){let{from:o,to:l}=e.bounds,c=i.from,d=null;(s===8||Fe.android&&e.text.length=i.from&&n.to<=i.to&&(n.from!=i.from||n.to!=i.to)&&i.to-i.from-(n.to-n.from)<=4?n={from:i.from,to:i.to,insert:t.state.doc.slice(i.from,n.from).append(n.insert).append(t.state.doc.slice(n.to,i.to))}:Fe.chrome&&n&&n.from==n.to&&n.from==i.head&&n.insert.toString()==` `&&t.lineWrapping&&(r&&(r=Pe.single(r.main.anchor-1,r.main.head-1)),n={from:i.from,to:i.to,insert:Lt.of([" "])}),n)return O1(t,n,r,s);if(r&&!r.main.eq(i)){let o=!1,l="select";return t.inputState.lastSelectionTime>Date.now()-50&&(t.inputState.lastSelectionOrigin=="select"&&(o=!0),l=t.inputState.lastSelectionOrigin),t.dispatch({selection:r,scrollIntoView:o,userEvent:l}),!0}else return!1}function O1(t,e,n,r=-1){if(Fe.ios&&t.inputState.flushIOSKey(e))return!0;let i=t.state.selection.main;if(Fe.android&&(e.to==i.to&&(e.from==i.from||e.from==i.from-1&&t.state.sliceDoc(e.from,i.from)==" ")&&e.insert.length==1&&e.insert.lines==2&&cl(t.contentDOM,"Enter",13)||(e.from==i.from-1&&e.to==i.to&&e.insert.length==0||r==8&&e.insert.lengthi.head)&&cl(t.contentDOM,"Backspace",8)||e.from==i.from&&e.to==i.to+1&&e.insert.length==0&&cl(t.contentDOM,"Delete",46)))return!0;let s=e.insert.toString();t.inputState.composing>=0&&t.inputState.composing++;let o,l=()=>o||(o=jY(t,e,n));return t.state.facet(yI).some(c=>c(t,e.from,e.to,s,l))||t.dispatch(l()),!0}function jY(t,e,n){let r,i=t.state,s=i.selection.main;if(e.from>=s.from&&e.to<=s.to&&e.to-e.from>=(s.to-s.from)/3&&(!n||n.main.empty&&n.main.from==e.from+e.insert.length)&&t.inputState.composing<0){let l=s.frome.to?i.sliceDoc(e.to,s.to):"";r=i.replaceSelection(t.state.toText(l+e.insert.sliceString(0,void 0,t.state.lineBreak)+c))}else{let l=i.changes(e),c=n&&n.main.to<=l.newLength?n.main:void 0;if(i.selection.ranges.length>1&&t.inputState.composing>=0&&e.to<=s.to&&e.to>=s.to-10){let d=t.state.sliceDoc(e.from,e.to),f,p=n&&NI(t,n.main.head);if(p){let x=e.insert.length-(e.to-e.from);f={from:p.from,to:p.to-x}}else f=t.state.doc.lineAt(s.head);let m=s.to-e.to,g=s.to-s.from;r=i.changeByRange(x=>{if(x.from==s.from&&x.to==s.to)return{changes:l,range:c||x.map(l)};let v=x.to-m,S=v-d.length;if(x.to-x.from!=g||t.state.sliceDoc(S,v)!=d||x.to>=f.from&&x.from<=f.to)return{range:x};let C=i.changes({from:S,to:v,insert:e.insert}),A=x.to-s.to;return{changes:C,range:c?Pe.range(Math.max(0,c.anchor+A),Math.max(0,c.head+A)):x.map(C)}})}else r={changes:l,selection:c&&i.selection.replaceRange(c)}}let o="input.type";return(t.composing||t.inputState.compositionPendingChange&&t.inputState.compositionEndedAt>Date.now()-50)&&(t.inputState.compositionPendingChange=!1,o+=".compose",t.inputState.compositionFirstChange&&(o+=".start",t.inputState.compositionFirstChange=!1)),i.update(r,{userEvent:o,scrollIntoView:!0})}function $Y(t,e,n,r){let i=Math.min(t.length,e.length),s=0;for(;s0&&l>0&&t.charCodeAt(o-1)==e.charCodeAt(l-1);)o--,l--;if(r=="end"){let c=Math.max(0,s-Math.min(o,l));n-=o+c-s}if(o=o?s-n:0;s-=c,l=s+(l-o),o=s}else if(l=l?s-n:0;s-=c,o=s+(o-l),l=s}return{from:s,toA:o,toB:l}}function WY(t){let e=[];if(t.root.activeElement!=t.contentDOM)return e;let{anchorNode:n,anchorOffset:r,focusNode:i,focusOffset:s}=t.observer.selectionRange;return n&&(e.push(new xS(n,r)),(i!=n||s!=r)&&e.push(new xS(i,s))),e}function VY(t,e){if(t.length==0)return null;let n=t[0].pos,r=t.length==2?t[1].pos:n;return n>-1&&r>-1?Pe.single(n+e,r+e):null}class GY{setSelectionOrigin(e){this.lastSelectionOrigin=e,this.lastSelectionTime=Date.now()}constructor(e){this.view=e,this.lastKeyCode=0,this.lastKeyTime=0,this.lastTouchTime=0,this.lastFocusTime=0,this.lastScrollTop=0,this.lastScrollLeft=0,this.pendingIOSKey=void 0,this.tabFocusMode=-1,this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastContextMenu=0,this.scrollHandlers=[],this.handlers=Object.create(null),this.composing=-1,this.compositionFirstChange=null,this.compositionEndedAt=0,this.compositionPendingKey=!1,this.compositionPendingChange=!1,this.mouseSelection=null,this.draggedContent=null,this.handleEvent=this.handleEvent.bind(this),this.notifiedFocused=e.hasFocus,Fe.safari&&e.contentDOM.addEventListener("input",()=>null),Fe.gecko&&lq(e.contentDOM.ownerDocument)}handleEvent(e){!eq(this.view,e)||this.ignoreDuringComposition(e)||e.type=="keydown"&&this.keydown(e)||(this.view.updateState!=0?Promise.resolve().then(()=>this.runHandlers(e.type,e)):this.runHandlers(e.type,e))}runHandlers(e,n){let r=this.handlers[e];if(r){for(let i of r.observers)i(this.view,n);for(let i of r.handlers){if(n.defaultPrevented)break;if(i(this.view,n)){n.preventDefault();break}}}}ensureHandlers(e){let n=KY(e),r=this.handlers,i=this.view.contentDOM;for(let s in n)if(s!="scroll"){let o=!n[s].handlers.length,l=r[s];l&&o!=!l.handlers.length&&(i.removeEventListener(s,this.handleEvent),l=null),l||i.addEventListener(s,this.handleEvent,{passive:o})}for(let s in r)s!="scroll"&&!n[s]&&i.removeEventListener(s,this.handleEvent);this.handlers=n}keydown(e){if(this.lastKeyCode=e.keyCode,this.lastKeyTime=Date.now(),e.keyCode==9&&this.tabFocusMode>-1&&(!this.tabFocusMode||Date.now()<=this.tabFocusMode))return!0;if(this.tabFocusMode>0&&e.keyCode!=27&&MI.indexOf(e.keyCode)<0&&(this.tabFocusMode=-1),Fe.android&&Fe.chrome&&!e.synthetic&&(e.keyCode==13||e.keyCode==8))return this.view.observer.delayAndroidKey(e.key,e.keyCode),!0;let n;return Fe.ios&&!e.synthetic&&!e.altKey&&!e.metaKey&&((n=OI.find(r=>r.keyCode==e.keyCode))&&!e.ctrlKey||YY.indexOf(e.key)>-1&&e.ctrlKey&&!e.shiftKey)?(this.pendingIOSKey=n||e,setTimeout(()=>this.flushIOSKey(),250),!0):(e.keyCode!=229&&this.view.observer.forceFlush(),!1)}flushIOSKey(e){let n=this.pendingIOSKey;return!n||n.key=="Enter"&&e&&e.from0?!0:Fe.safari&&!Fe.ios&&this.compositionPendingKey&&Date.now()-this.compositionEndedAt<100?(this.compositionPendingKey=!1,!0):!1:!1}startMouseSelection(e){this.mouseSelection&&this.mouseSelection.destroy(),this.mouseSelection=e}update(e){this.view.observer.update(e),this.mouseSelection&&this.mouseSelection.update(e),this.draggedContent&&e.docChanged&&(this.draggedContent=this.draggedContent.map(e.changes)),e.transactions.length&&(this.lastKeyCode=this.lastSelectionTime=0)}destroy(){this.mouseSelection&&this.mouseSelection.destroy()}}function vS(t,e){return(n,r)=>{try{return e.call(t,r,n)}catch(i){gs(n.state,i)}}}function KY(t){let e=Object.create(null);function n(r){return e[r]||(e[r]={observers:[],handlers:[]})}for(let r of t){let i=r.spec,s=i&&i.plugin.domEventHandlers,o=i&&i.plugin.domEventObservers;if(s)for(let l in s){let c=s[l];c&&n(l).handlers.push(vS(r.value,c))}if(o)for(let l in o){let c=o[l];c&&n(l).observers.push(vS(r.value,c))}}for(let r in Ci)n(r).handlers.push(Ci[r]);for(let r in oi)n(r).observers.push(oi[r]);return e}const OI=[{key:"Backspace",keyCode:8,inputType:"deleteContentBackward"},{key:"Enter",keyCode:13,inputType:"insertParagraph"},{key:"Enter",keyCode:13,inputType:"insertLineBreak"},{key:"Delete",keyCode:46,inputType:"deleteContentForward"}],YY="dthko",MI=[16,17,18,20,91,92,224,225],_f=6;function Cf(t){return Math.max(0,t)*.7+8}function qY(t,e){return Math.max(Math.abs(t.clientX-e.clientX),Math.abs(t.clientY-e.clientY))}class XY{constructor(e,n,r,i){this.view=e,this.startEvent=n,this.style=r,this.mustSelect=i,this.scrollSpeed={x:0,y:0},this.scrolling=-1,this.lastEvent=n,this.scrollParents=tY(e.contentDOM),this.atoms=e.state.facet(I1).map(o=>o(e));let s=e.contentDOM.ownerDocument;s.addEventListener("mousemove",this.move=this.move.bind(this)),s.addEventListener("mouseup",this.up=this.up.bind(this)),this.extend=n.shiftKey,this.multiple=e.state.facet(Wt.allowMultipleSelections)&&QY(e,n),this.dragging=JY(e,n)&&PI(n)==1?null:!1}start(e){this.dragging===!1&&this.select(e)}move(e){if(e.buttons==0)return this.destroy();if(this.dragging||this.dragging==null&&qY(this.startEvent,e)<10)return;this.select(this.lastEvent=e);let n=0,r=0,i=0,s=0,o=this.view.win.innerWidth,l=this.view.win.innerHeight;this.scrollParents.x&&({left:i,right:o}=this.scrollParents.x.getBoundingClientRect()),this.scrollParents.y&&({top:s,bottom:l}=this.scrollParents.y.getBoundingClientRect());let c=kI(this.view);e.clientX-c.left<=i+_f?n=-Cf(i-e.clientX):e.clientX+c.right>=o-_f&&(n=Cf(e.clientX-o)),e.clientY-c.top<=s+_f?r=-Cf(s-e.clientY):e.clientY+c.bottom>=l-_f&&(r=Cf(e.clientY-l)),this.setScrollSpeed(n,r)}up(e){this.dragging==null&&this.select(this.lastEvent),this.dragging||e.preventDefault(),this.destroy()}destroy(){this.setScrollSpeed(0,0);let e=this.view.contentDOM.ownerDocument;e.removeEventListener("mousemove",this.move),e.removeEventListener("mouseup",this.up),this.view.inputState.mouseSelection=this.view.inputState.draggedContent=null}setScrollSpeed(e,n){this.scrollSpeed={x:e,y:n},e||n?this.scrolling<0&&(this.scrolling=setInterval(()=>this.scroll(),50)):this.scrolling>-1&&(clearInterval(this.scrolling),this.scrolling=-1)}scroll(){let{x:e,y:n}=this.scrollSpeed;e&&this.scrollParents.x&&(this.scrollParents.x.scrollLeft+=e,e=0),n&&this.scrollParents.y&&(this.scrollParents.y.scrollTop+=n,n=0),(e||n)&&this.view.win.scrollBy(e,n),this.dragging===!1&&this.select(this.lastEvent)}skipAtoms(e){let n=null;for(let r=0;rn.isUserEvent("input.type"))?this.destroy():this.style.update(e)&&setTimeout(()=>this.select(this.lastEvent),20)}}function QY(t,e){let n=t.state.facet(mI);return n.length?n[0](e):Fe.mac?e.metaKey:e.ctrlKey}function ZY(t,e){let n=t.state.facet(gI);return n.length?n[0](e):Fe.mac?!e.altKey:!e.ctrlKey}function JY(t,e){let{main:n}=t.state.selection;if(n.empty)return!1;let r=Tc(t.root);if(!r||r.rangeCount==0)return!0;let i=r.getRangeAt(0).getClientRects();for(let s=0;s=e.clientX&&o.top<=e.clientY&&o.bottom>=e.clientY)return!0}return!1}function eq(t,e){if(!e.bubbles)return!0;if(e.defaultPrevented)return!1;for(let n=e.target,r;n!=t.contentDOM;n=n.parentNode)if(!n||n.nodeType==11||(r=Gt.get(n))&&r.ignoreEvent(e))return!1;return!0}const Ci=Object.create(null),oi=Object.create(null),DI=Fe.ie&&Fe.ie_version<15||Fe.ios&&Fe.webkit_version<604;function tq(t){let e=t.dom.parentNode;if(!e)return;let n=e.appendChild(document.createElement("textarea"));n.style.cssText="position: fixed; left: -10000px; top: 10px",n.focus(),setTimeout(()=>{t.focus(),n.remove(),LI(t,n.value)},50)}function Ap(t,e,n){for(let r of t.facet(e))n=r(n,t);return n}function LI(t,e){e=Ap(t.state,k1,e);let{state:n}=t,r,i=1,s=n.toText(e),o=s.lines==n.selection.ranges.length;if(jb!=null&&n.selection.ranges.every(c=>c.empty)&&jb==s.toString()){let c=-1;r=n.changeByRange(d=>{let f=n.doc.lineAt(d.from);if(f.from==c)return{range:d};c=f.from;let p=n.toText((o?s.line(i++).text:e)+n.lineBreak);return{changes:{from:f.from,insert:p},range:Pe.cursor(d.from+p.length)}})}else o?r=n.changeByRange(c=>{let d=s.line(i++);return{changes:{from:c.from,to:c.to,insert:d.text},range:Pe.cursor(c.from+d.length)}}):r=n.replaceSelection(s);t.dispatch(r,{userEvent:"input.paste",scrollIntoView:!0})}oi.scroll=t=>{t.inputState.lastScrollTop=t.scrollDOM.scrollTop,t.inputState.lastScrollLeft=t.scrollDOM.scrollLeft};Ci.keydown=(t,e)=>(t.inputState.setSelectionOrigin("select"),e.keyCode==27&&t.inputState.tabFocusMode!=0&&(t.inputState.tabFocusMode=Date.now()+2e3),!1);oi.touchstart=(t,e)=>{t.inputState.lastTouchTime=Date.now(),t.inputState.setSelectionOrigin("select.pointer")};oi.touchmove=t=>{t.inputState.setSelectionOrigin("select.pointer")};Ci.mousedown=(t,e)=>{if(t.observer.flush(),t.inputState.lastTouchTime>Date.now()-2e3)return!1;let n=null;for(let r of t.state.facet(bI))if(n=r(t,e),n)break;if(!n&&e.button==0&&(n=iq(t,e)),n){let r=!t.hasFocus;t.inputState.startMouseSelection(new XY(t,e,n,r)),r&&t.observer.ignore(()=>{XR(t.contentDOM);let s=t.root.activeElement;s&&!s.contains(t.contentDOM)&&s.blur()});let i=t.inputState.mouseSelection;if(i)return i.start(e),i.dragging===!1}return!1};function wS(t,e,n,r){if(r==1)return Pe.cursor(e,n);if(r==2)return RY(t.state,e,n);{let i=In.find(t.docView,e),s=t.state.doc.lineAt(i?i.posAtEnd:e),o=i?i.posAtStart:s.from,l=i?i.posAtEnd:s.to;return le>=n.top&&e<=n.bottom&&t>=n.left&&t<=n.right;function nq(t,e,n,r){let i=In.find(t.docView,e);if(!i)return 1;let s=e-i.posAtStart;if(s==0)return 1;if(s==i.length)return-1;let o=i.coordsAt(s,-1);if(o&&TS(n,r,o))return-1;let l=i.coordsAt(s,1);return l&&TS(n,r,l)?1:o&&o.bottom>=r?-1:1}function SS(t,e){let n=t.posAtCoords({x:e.clientX,y:e.clientY},!1);return{pos:n,bias:nq(t,n,e.clientX,e.clientY)}}const rq=Fe.ie&&Fe.ie_version<=11;let _S=null,CS=0,AS=0;function PI(t){if(!rq)return t.detail;let e=_S,n=AS;return _S=t,AS=Date.now(),CS=!e||n>Date.now()-400&&Math.abs(e.clientX-t.clientX)<2&&Math.abs(e.clientY-t.clientY)<2?(CS+1)%3:1}function iq(t,e){let n=SS(t,e),r=PI(e),i=t.state.selection;return{update(s){s.docChanged&&(n.pos=s.changes.mapPos(n.pos),i=i.map(s.changes))},get(s,o,l){let c=SS(t,s),d,f=wS(t,c.pos,c.bias,r);if(n.pos!=c.pos&&!o){let p=wS(t,n.pos,n.bias,r),m=Math.min(p.from,f.from),g=Math.max(p.to,f.to);f=m1&&(d=sq(i,c.pos))?d:l?i.addRange(f):Pe.create([f])}}}function sq(t,e){for(let n=0;n=e)return Pe.create(t.ranges.slice(0,n).concat(t.ranges.slice(n+1)),t.mainIndex==n?0:t.mainIndex-(t.mainIndex>n?1:0))}return null}Ci.dragstart=(t,e)=>{let{selection:{main:n}}=t.state;if(e.target.draggable){let i=t.docView.nearest(e.target);if(i&&i.isWidget){let s=i.posAtStart,o=s+i.length;(s>=n.to||o<=n.from)&&(n=Pe.range(s,o))}}let{inputState:r}=t;return r.mouseSelection&&(r.mouseSelection.dragging=!0),r.draggedContent=n,e.dataTransfer&&(e.dataTransfer.setData("Text",Ap(t.state,N1,t.state.sliceDoc(n.from,n.to))),e.dataTransfer.effectAllowed="copyMove"),!1};Ci.dragend=t=>(t.inputState.draggedContent=null,!1);function kS(t,e,n,r){if(n=Ap(t.state,k1,n),!n)return;let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1),{draggedContent:s}=t.inputState,o=r&&s&&ZY(t,e)?{from:s.from,to:s.to}:null,l={from:i,insert:n},c=t.state.changes(o?[o,l]:l);t.focus(),t.dispatch({changes:c,selection:{anchor:c.mapPos(i,-1),head:c.mapPos(i,1)},userEvent:o?"move.drop":"input.drop"}),t.inputState.draggedContent=null}Ci.drop=(t,e)=>{if(!e.dataTransfer)return!1;if(t.state.readOnly)return!0;let n=e.dataTransfer.files;if(n&&n.length){let r=Array(n.length),i=0,s=()=>{++i==n.length&&kS(t,e,r.filter(o=>o!=null).join(t.state.lineBreak),!1)};for(let o=0;o{/[\x00-\x08\x0e-\x1f]{2}/.test(l.result)||(r[o]=l.result),s()},l.readAsText(n[o])}return!0}else{let r=e.dataTransfer.getData("Text");if(r)return kS(t,e,r,!0),!0}return!1};Ci.paste=(t,e)=>{if(t.state.readOnly)return!0;t.observer.flush();let n=DI?null:e.clipboardData;return n?(LI(t,n.getData("text/plain")||n.getData("text/uri-list")),!0):(tq(t),!1)};function oq(t,e){let n=t.dom.parentNode;if(!n)return;let r=n.appendChild(document.createElement("textarea"));r.style.cssText="position: fixed; left: -10000px; top: 10px",r.value=e,r.focus(),r.selectionEnd=e.length,r.selectionStart=0,setTimeout(()=>{r.remove(),t.focus()},50)}function aq(t){let e=[],n=[],r=!1;for(let i of t.selection.ranges)i.empty||(e.push(t.sliceDoc(i.from,i.to)),n.push(i));if(!e.length){let i=-1;for(let{from:s}of t.selection.ranges){let o=t.doc.lineAt(s);o.number>i&&(e.push(o.text),n.push({from:o.from,to:Math.min(t.doc.length,o.to+1)})),i=o.number}r=!0}return{text:Ap(t,N1,e.join(t.lineBreak)),ranges:n,linewise:r}}let jb=null;Ci.copy=Ci.cut=(t,e)=>{let{text:n,ranges:r,linewise:i}=aq(t.state);if(!n&&!i)return!1;jb=i?n:null,e.type=="cut"&&!t.state.readOnly&&t.dispatch({changes:r,scrollIntoView:!0,userEvent:"delete.cut"});let s=DI?null:e.clipboardData;return s?(s.clearData(),s.setData("text/plain",n),!0):(oq(t,n),!1)};const FI=Hl.define();function BI(t,e){let n=[];for(let r of t.facet(xI)){let i=r(t,e);i&&n.push(i)}return n.length?t.update({effects:n,annotations:FI.of(!0)}):null}function UI(t){setTimeout(()=>{let e=t.hasFocus;if(e!=t.inputState.notifiedFocused){let n=BI(t.state,e);n?t.dispatch(n):t.update([])}},10)}oi.focus=t=>{t.inputState.lastFocusTime=Date.now(),!t.scrollDOM.scrollTop&&(t.inputState.lastScrollTop||t.inputState.lastScrollLeft)&&(t.scrollDOM.scrollTop=t.inputState.lastScrollTop,t.scrollDOM.scrollLeft=t.inputState.lastScrollLeft),UI(t)};oi.blur=t=>{t.observer.clearSelectionRange(),UI(t)};oi.compositionstart=oi.compositionupdate=t=>{t.observer.editContext||(t.inputState.compositionFirstChange==null&&(t.inputState.compositionFirstChange=!0),t.inputState.composing<0&&(t.inputState.composing=0))};oi.compositionend=t=>{t.observer.editContext||(t.inputState.composing=-1,t.inputState.compositionEndedAt=Date.now(),t.inputState.compositionPendingKey=!0,t.inputState.compositionPendingChange=t.observer.pendingRecords().length>0,t.inputState.compositionFirstChange=null,Fe.chrome&&Fe.android?t.observer.flushSoon():t.inputState.compositionPendingChange?Promise.resolve().then(()=>t.observer.flush()):setTimeout(()=>{t.inputState.composing<0&&t.docView.hasComposition&&t.update([])},50))};oi.contextmenu=t=>{t.inputState.lastContextMenu=Date.now()};Ci.beforeinput=(t,e)=>{var n,r;if(e.inputType=="insertReplacementText"&&t.observer.editContext){let s=(n=e.dataTransfer)===null||n===void 0?void 0:n.getData("text/plain"),o=e.getTargetRanges();if(s&&o.length){let l=o[0],c=t.posAtDOM(l.startContainer,l.startOffset),d=t.posAtDOM(l.endContainer,l.endOffset);return O1(t,{from:c,to:d,insert:t.state.toText(s)},null),!0}}let i;if(Fe.chrome&&Fe.android&&(i=OI.find(s=>s.inputType==e.inputType))&&(t.observer.delayAndroidKey(i.key,i.keyCode),i.key=="Backspace"||i.key=="Delete")){let s=((r=window.visualViewport)===null||r===void 0?void 0:r.height)||0;setTimeout(()=>{var o;(((o=window.visualViewport)===null||o===void 0?void 0:o.height)||0)>s+10&&t.hasFocus&&(t.contentDOM.blur(),t.focus())},100)}return Fe.ios&&e.inputType=="deleteContentForward"&&t.observer.flushSoon(),Fe.safari&&e.inputType=="insertText"&&t.inputState.composing>=0&&setTimeout(()=>oi.compositionend(t,e),20),!1};const NS=new Set;function lq(t){NS.has(t)||(NS.add(t),t.addEventListener("copy",()=>{}),t.addEventListener("cut",()=>{}))}const RS=["pre-wrap","normal","pre-line","break-spaces"];let _l=!1;function IS(){_l=!1}class uq{constructor(e){this.lineWrapping=e,this.doc=Lt.empty,this.heightSamples={},this.lineHeight=14,this.charWidth=7,this.textHeight=14,this.lineLength=30}heightForGap(e,n){let r=this.doc.lineAt(n).number-this.doc.lineAt(e).number+1;return this.lineWrapping&&(r+=Math.max(0,Math.ceil((n-e-r*this.lineLength*.5)/this.lineLength))),this.lineHeight*r}heightForLine(e){return this.lineWrapping?(1+Math.max(0,Math.ceil((e-this.lineLength)/Math.max(1,this.lineLength-5))))*this.lineHeight:this.lineHeight}setDoc(e){return this.doc=e,this}mustRefreshForWrapping(e){return RS.indexOf(e)>-1!=this.lineWrapping}mustRefreshForHeights(e){let n=!1;for(let r=0;r-1,c=Math.round(n)!=Math.round(this.lineHeight)||this.lineWrapping!=l;if(this.lineWrapping=l,this.lineHeight=n,this.charWidth=r,this.textHeight=i,this.lineLength=s,c){this.heightSamples={};for(let d=0;d0}set outdated(e){this.flags=(e?2:0)|this.flags&-3}setHeight(e){this.height!=e&&(Math.abs(this.height-e)>Kf&&(_l=!0),this.height=e)}replace(e,n,r){return yr.of(r)}decomposeLeft(e,n){n.push(this)}decomposeRight(e,n){n.push(this)}applyChanges(e,n,r,i){let s=this,o=r.doc;for(let l=i.length-1;l>=0;l--){let{fromA:c,toA:d,fromB:f,toB:p}=i[l],m=s.lineAt(c,sn.ByPosNoHeight,r.setDoc(n),0,0),g=m.to>=d?m:s.lineAt(d,sn.ByPosNoHeight,r,0,0);for(p+=g.to-d,d=g.to;l>0&&m.from<=i[l-1].toA;)c=i[l-1].fromA,f=i[l-1].fromB,l--,cs*2){let l=e[n-1];l.break?e.splice(--n,1,l.left,null,l.right):e.splice(--n,1,l.left,l.right),r+=1+l.break,i-=l.size}else if(s>i*2){let l=e[r];l.break?e.splice(r,1,l.left,null,l.right):e.splice(r,1,l.left,l.right),r+=2+l.break,s-=l.size}else break;else if(i=s&&o(this.blockAt(0,r,i,s))}updateHeight(e,n=0,r=!1,i){return i&&i.from<=n&&i.more&&this.setHeight(i.heights[i.index++]),this.outdated=!1,this}toString(){return`block(${this.length})`}}class jr extends HI{constructor(e,n){super(e,n,null),this.collapsed=0,this.widgetHeight=0,this.breaks=0}blockAt(e,n,r,i){return new Wi(i,this.length,r,this.height,this.breaks)}replace(e,n,r){let i=r[0];return r.length==1&&(i instanceof jr||i instanceof Yn&&i.flags&4)&&Math.abs(this.length-i.length)<10?(i instanceof Yn?i=new jr(i.length,this.height):i.height=this.height,this.outdated||(i.outdated=!1),i):yr.of(r)}updateHeight(e,n=0,r=!1,i){return i&&i.from<=n&&i.more?this.setHeight(i.heights[i.index++]):(r||this.outdated)&&this.setHeight(Math.max(this.widgetHeight,e.heightForLine(this.length-this.collapsed))+this.breaks*e.lineHeight),this.outdated=!1,this}toString(){return`line(${this.length}${this.collapsed?-this.collapsed:""}${this.widgetHeight?":"+this.widgetHeight:""})`}}class Yn extends yr{constructor(e){super(e,0)}heightMetrics(e,n){let r=e.doc.lineAt(n).number,i=e.doc.lineAt(n+this.length).number,s=i-r+1,o,l=0;if(e.lineWrapping){let c=Math.min(this.height,e.lineHeight*s);o=c/s,this.length>s+1&&(l=(this.height-c)/(this.length-s-1))}else o=this.height/s;return{firstLine:r,lastLine:i,perLine:o,perChar:l}}blockAt(e,n,r,i){let{firstLine:s,lastLine:o,perLine:l,perChar:c}=this.heightMetrics(n,i);if(n.lineWrapping){let d=i+(e0){let s=r[r.length-1];s instanceof Yn?r[r.length-1]=new Yn(s.length+i):r.push(null,new Yn(i-1))}if(e>0){let s=r[0];s instanceof Yn?r[0]=new Yn(e+s.length):r.unshift(new Yn(e-1),null)}return yr.of(r)}decomposeLeft(e,n){n.push(new Yn(e-1),null)}decomposeRight(e,n){n.push(null,new Yn(this.length-e-1))}updateHeight(e,n=0,r=!1,i){let s=n+this.length;if(i&&i.from<=n+this.length&&i.more){let o=[],l=Math.max(n,i.from),c=-1;for(i.from>n&&o.push(new Yn(i.from-n-1).updateHeight(e,n));l<=s&&i.more;){let f=e.doc.lineAt(l).length;o.length&&o.push(null);let p=i.heights[i.index++];c==-1?c=p:Math.abs(p-c)>=Kf&&(c=-2);let m=new jr(f,p);m.outdated=!1,o.push(m),l+=f+1}l<=s&&o.push(null,new Yn(s-l).updateHeight(e,l));let d=yr.of(o);return(c<0||Math.abs(d.height-this.height)>=Kf||Math.abs(c-this.heightMetrics(e,n).perLine)>=Kf)&&(_l=!0),_h(this,d)}else(r||this.outdated)&&(this.setHeight(e.heightForGap(n,n+this.length)),this.outdated=!1);return this}toString(){return`gap(${this.length})`}}class dq extends yr{constructor(e,n,r){super(e.length+n+r.length,e.height+r.height,n|(e.outdated||r.outdated?2:0)),this.left=e,this.right=r,this.size=e.size+r.size}get break(){return this.flags&1}blockAt(e,n,r,i){let s=r+this.left.height;return el))return d;let f=n==sn.ByPosNoHeight?sn.ByPosNoHeight:sn.ByPos;return c?d.join(this.right.lineAt(l,f,r,o,l)):this.left.lineAt(l,f,r,i,s).join(d)}forEachLine(e,n,r,i,s,o){let l=i+this.left.height,c=s+this.left.length+this.break;if(this.break)e=c&&this.right.forEachLine(e,n,r,l,c,o);else{let d=this.lineAt(c,sn.ByPos,r,i,s);e=e&&d.from<=n&&o(d),n>d.to&&this.right.forEachLine(d.to+1,n,r,l,c,o)}}replace(e,n,r){let i=this.left.length+this.break;if(nthis.left.length)return this.balanced(this.left,this.right.replace(e-i,n-i,r));let s=[];e>0&&this.decomposeLeft(e,s);let o=s.length;for(let l of r)s.push(l);if(e>0&&OS(s,o-1),n=r&&n.push(null)),e>r&&this.right.decomposeLeft(e-r,n)}decomposeRight(e,n){let r=this.left.length,i=r+this.break;if(e>=i)return this.right.decomposeRight(e-i,n);e2*n.size||n.size>2*e.size?yr.of(this.break?[e,null,n]:[e,n]):(this.left=_h(this.left,e),this.right=_h(this.right,n),this.setHeight(e.height+n.height),this.outdated=e.outdated||n.outdated,this.size=e.size+n.size,this.length=e.length+this.break+n.length,this)}updateHeight(e,n=0,r=!1,i){let{left:s,right:o}=this,l=n+s.length+this.break,c=null;return i&&i.from<=n+s.length&&i.more?c=s=s.updateHeight(e,n,r,i):s.updateHeight(e,n,r),i&&i.from<=l+o.length&&i.more?c=o=o.updateHeight(e,l,r,i):o.updateHeight(e,l,r),c?this.balanced(s,o):(this.height=this.left.height+this.right.height,this.outdated=!1,this)}toString(){return this.left+(this.break?" ":"-")+this.right}}function OS(t,e){let n,r;t[e]==null&&(n=t[e-1])instanceof Yn&&(r=t[e+1])instanceof Yn&&t.splice(e-1,3,new Yn(n.length+1+r.length))}const fq=5;class M1{constructor(e,n){this.pos=e,this.oracle=n,this.nodes=[],this.lineStart=-1,this.lineEnd=-1,this.covering=null,this.writtenTo=e}get isCovered(){return this.covering&&this.nodes[this.nodes.length-1]==this.covering}span(e,n){if(this.lineStart>-1){let r=Math.min(n,this.lineEnd),i=this.nodes[this.nodes.length-1];i instanceof jr?i.length+=r-this.pos:(r>this.pos||!this.isCovered)&&this.nodes.push(new jr(r-this.pos,-1)),this.writtenTo=r,n>r&&(this.nodes.push(null),this.writtenTo++,this.lineStart=-1)}this.pos=n}point(e,n,r){if(e=fq)&&this.addLineDeco(i,s,o)}else n>e&&this.span(e,n);this.lineEnd>-1&&this.lineEnd-1)return;let{from:e,to:n}=this.oracle.doc.lineAt(this.pos);this.lineStart=e,this.lineEnd=n,this.writtenToe&&this.nodes.push(new jr(this.pos-e,-1)),this.writtenTo=this.pos}blankContent(e,n){let r=new Yn(n-e);return this.oracle.doc.lineAt(e).to==n&&(r.flags|=4),r}ensureLine(){this.enterLine();let e=this.nodes.length?this.nodes[this.nodes.length-1]:null;if(e instanceof jr)return e;let n=new jr(0,-1);return this.nodes.push(n),n}addBlock(e){this.enterLine();let n=e.deco;n&&n.startSide>0&&!this.isCovered&&this.ensureLine(),this.nodes.push(e),this.writtenTo=this.pos=this.pos+e.length,n&&n.endSide>0&&(this.covering=e)}addLineDeco(e,n,r){let i=this.ensureLine();i.length+=r,i.collapsed+=r,i.widgetHeight=Math.max(i.widgetHeight,e),i.breaks+=n,this.writtenTo=this.pos=this.pos+r}finish(e){let n=this.nodes.length==0?null:this.nodes[this.nodes.length-1];this.lineStart>-1&&!(n instanceof jr)&&!this.isCovered?this.nodes.push(new jr(0,-1)):(this.writtenTof.clientHeight||f.scrollWidth>f.clientWidth)&&p.overflow!="visible"){let m=f.getBoundingClientRect();s=Math.max(s,m.left),o=Math.min(o,m.right),l=Math.max(l,m.top),c=Math.min(d==t.parentNode?i.innerHeight:c,m.bottom)}d=p.position=="absolute"||p.position=="fixed"?f.offsetParent:f.parentNode}else if(d.nodeType==11)d=d.host;else break;return{left:s-n.left,right:Math.max(s,o)-n.left,top:l-(n.top+e),bottom:Math.max(l,c)-(n.top+e)}}function gq(t){let e=t.getBoundingClientRect(),n=t.ownerDocument.defaultView||window;return e.left0&&e.top0}function bq(t,e){let n=t.getBoundingClientRect();return{left:0,right:n.right-n.left,top:e,bottom:n.bottom-(n.top+e)}}class y0{constructor(e,n,r,i){this.from=e,this.to=n,this.size=r,this.displaySize=i}static same(e,n){if(e.length!=n.length)return!1;for(let r=0;rtypeof r!="function"&&r.class=="cm-lineWrapping");this.heightOracle=new uq(n),this.stateDeco=e.facet(_c).filter(r=>typeof r!="function"),this.heightMap=yr.empty().applyChanges(this.stateDeco,Lt.empty,this.heightOracle.setDoc(e.doc),[new si(0,0,0,e.doc.length)]);for(let r=0;r<2&&(this.viewport=this.getViewport(0,null),!!this.updateForViewport());r++);this.updateViewportLines(),this.lineGaps=this.ensureLineGaps([]),this.lineGapDeco=en.set(this.lineGaps.map(r=>r.draw(this,!1))),this.computeVisibleRanges()}updateForViewport(){let e=[this.viewport],{main:n}=this.state.selection;for(let r=0;r<=1;r++){let i=r?n.head:n.anchor;if(!e.some(({from:s,to:o})=>i>=s&&i<=o)){let{from:s,to:o}=this.lineBlockAt(i);e.push(new Af(s,o))}}return this.viewports=e.sort((r,i)=>r.from-i.from),this.updateScaler()}updateScaler(){let e=this.scaler;return this.scaler=this.heightMap.height<=7e6?DS:new D1(this.heightOracle,this.heightMap,this.viewports),e.eq(this.scaler)?0:2}updateViewportLines(){this.viewportLines=[],this.heightMap.forEachLine(this.viewport.from,this.viewport.to,this.heightOracle.setDoc(this.state.doc),0,0,e=>{this.viewportLines.push(Vu(e,this.scaler))})}update(e,n=null){this.state=e.state;let r=this.stateDeco;this.stateDeco=this.state.facet(_c).filter(f=>typeof f!="function");let i=e.changedRanges,s=si.extendWithRanges(i,hq(r,this.stateDeco,e?e.changes:$n.empty(this.state.doc.length))),o=this.heightMap.height,l=this.scrolledToBottom?null:this.scrollAnchorAt(this.scrollTop);IS(),this.heightMap=this.heightMap.applyChanges(this.stateDeco,e.startState.doc,this.heightOracle.setDoc(this.state.doc),s),(this.heightMap.height!=o||_l)&&(e.flags|=2),l?(this.scrollAnchorPos=e.changes.mapPos(l.from,-1),this.scrollAnchorHeight=l.top):(this.scrollAnchorPos=-1,this.scrollAnchorHeight=o);let c=s.length?this.mapViewport(this.viewport,e.changes):this.viewport;(n&&(n.range.headc.to)||!this.viewportIsAppropriate(c))&&(c=this.getViewport(0,n));let d=c.from!=this.viewport.from||c.to!=this.viewport.to;this.viewport=c,e.flags|=this.updateForViewport(),(d||!e.changes.empty||e.flags&2)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps,e.changes))),e.flags|=this.computeVisibleRanges(e.changes),n&&(this.scrollTarget=n),!this.mustEnforceCursorAssoc&&e.selectionSet&&e.view.lineWrapping&&e.state.selection.main.empty&&e.state.selection.main.assoc&&!e.state.facet(vY)&&(this.mustEnforceCursorAssoc=!0)}measure(e){let n=e.contentDOM,r=window.getComputedStyle(n),i=this.heightOracle,s=r.whiteSpace;this.defaultTextDirection=r.direction=="rtl"?ar.RTL:ar.LTR;let o=this.heightOracle.mustRefreshForWrapping(s),l=n.getBoundingClientRect(),c=o||this.mustMeasureContent||this.contentDOMHeight!=l.height;this.contentDOMHeight=l.height,this.mustMeasureContent=!1;let d=0,f=0;if(l.width&&l.height){let{scaleX:M,scaleY:F}=qR(n,l);(M>.005&&Math.abs(this.scaleX-M)>.005||F>.005&&Math.abs(this.scaleY-F)>.005)&&(this.scaleX=M,this.scaleY=F,d|=16,o=c=!0)}let p=(parseInt(r.paddingTop)||0)*this.scaleY,m=(parseInt(r.paddingBottom)||0)*this.scaleY;(this.paddingTop!=p||this.paddingBottom!=m)&&(this.paddingTop=p,this.paddingBottom=m,d|=18),this.editorWidth!=e.scrollDOM.clientWidth&&(i.lineWrapping&&(c=!0),this.editorWidth=e.scrollDOM.clientWidth,d|=16);let g=e.scrollDOM.scrollTop*this.scaleY;this.scrollTop!=g&&(this.scrollAnchorHeight=-1,this.scrollTop=g),this.scrolledToBottom=ZR(e.scrollDOM);let x=(this.printing?bq:mq)(n,this.paddingTop),v=x.top-this.pixelViewport.top,S=x.bottom-this.pixelViewport.bottom;this.pixelViewport=x;let C=this.pixelViewport.bottom>this.pixelViewport.top&&this.pixelViewport.right>this.pixelViewport.left;if(C!=this.inView&&(this.inView=C,C&&(c=!0)),!this.inView&&!this.scrollTarget&&!gq(e.dom))return 0;let A=l.width;if((this.contentDOMWidth!=A||this.editorHeight!=e.scrollDOM.clientHeight)&&(this.contentDOMWidth=l.width,this.editorHeight=e.scrollDOM.clientHeight,d|=16),c){let M=e.docView.measureVisibleLineHeights(this.viewport);if(i.mustRefreshForHeights(M)&&(o=!0),o||i.lineWrapping&&Math.abs(A-this.contentDOMWidth)>i.charWidth){let{lineHeight:F,charWidth:I,textHeight:D}=e.docView.measureTextSize();o=F>0&&i.refresh(s,F,I,D,Math.max(5,A/I),M),o&&(e.docView.minWidth=0,d|=16)}v>0&&S>0?f=Math.max(v,S):v<0&&S<0&&(f=Math.min(v,S)),IS();for(let F of this.viewports){let I=F.from==this.viewport.from?M:e.docView.measureVisibleLineHeights(F);this.heightMap=(o?yr.empty().applyChanges(this.stateDeco,Lt.empty,this.heightOracle,[new si(0,0,0,e.state.doc.length)]):this.heightMap).updateHeight(i,0,o,new cq(F.from,I))}_l&&(d|=2)}let k=!this.viewportIsAppropriate(this.viewport,f)||this.scrollTarget&&(this.scrollTarget.range.headthis.viewport.to);return k&&(d&2&&(d|=this.updateScaler()),this.viewport=this.getViewport(f,this.scrollTarget),d|=this.updateForViewport()),(d&2||k)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(o?[]:this.lineGaps,e)),d|=this.computeVisibleRanges(),this.mustEnforceCursorAssoc&&(this.mustEnforceCursorAssoc=!1,e.docView.enforceCursorAssoc()),d}get visibleTop(){return this.scaler.fromDOM(this.pixelViewport.top)}get visibleBottom(){return this.scaler.fromDOM(this.pixelViewport.bottom)}getViewport(e,n){let r=.5-Math.max(-.5,Math.min(.5,e/1e3/2)),i=this.heightMap,s=this.heightOracle,{visibleTop:o,visibleBottom:l}=this,c=new Af(i.lineAt(o-r*1e3,sn.ByHeight,s,0,0).from,i.lineAt(l+(1-r)*1e3,sn.ByHeight,s,0,0).to);if(n){let{head:d}=n.range;if(dc.to){let f=Math.min(this.editorHeight,this.pixelViewport.bottom-this.pixelViewport.top),p=i.lineAt(d,sn.ByPos,s,0,0),m;n.y=="center"?m=(p.top+p.bottom)/2-f/2:n.y=="start"||n.y=="nearest"&&d=l+Math.max(10,Math.min(r,250)))&&i>o-2*1e3&&s>1,o=i<<1;if(this.defaultTextDirection!=ar.LTR&&!r)return[];let l=[],c=(f,p,m,g)=>{if(p-ff&&CC.from>=m.from&&C.to<=m.to&&Math.abs(C.from-f)C.fromA));if(!S){if(pk.from<=p&&k.to>=p)){let k=n.moveToLineBoundary(Pe.cursor(p),!1,!0).head;k>f&&(p=k)}let C=this.gapSize(m,f,p,g),A=r||C<2e6?C:2e6;S=new y0(f,p,C,A)}l.push(S)},d=f=>{if(f.length2e6)for(let I of e)I.from>=f.from&&I.fromf.from&&c(f.from,g,f,p),xn.draw(this,this.heightOracle.lineWrapping))))}computeVisibleRanges(e){let n=this.stateDeco;this.lineGaps.length&&(n=n.concat(this.lineGapDeco));let r=[];Ht.spans(n,this.viewport.from,this.viewport.to,{span(s,o){r.push({from:s,to:o})},point(){}},20);let i=0;if(r.length!=this.visibleRanges.length)i=12;else for(let s=0;s=this.viewport.from&&e<=this.viewport.to&&this.viewportLines.find(n=>n.from<=e&&n.to>=e)||Vu(this.heightMap.lineAt(e,sn.ByPos,this.heightOracle,0,0),this.scaler)}lineBlockAtHeight(e){return e>=this.viewportLines[0].top&&e<=this.viewportLines[this.viewportLines.length-1].bottom&&this.viewportLines.find(n=>n.top<=e&&n.bottom>=e)||Vu(this.heightMap.lineAt(this.scaler.fromDOM(e),sn.ByHeight,this.heightOracle,0,0),this.scaler)}scrollAnchorAt(e){let n=this.lineBlockAtHeight(e+8);return n.from>=this.viewport.from||this.viewportLines[0].top-e>200?n:this.viewportLines[0]}elementAtHeight(e){return Vu(this.heightMap.blockAt(this.scaler.fromDOM(e),this.heightOracle,0,0),this.scaler)}get docHeight(){return this.scaler.toDOM(this.heightMap.height)}get contentHeight(){return this.docHeight+this.paddingTop+this.paddingBottom}}class Af{constructor(e,n){this.from=e,this.to=n}}function yq(t,e,n){let r=[],i=t,s=0;return Ht.spans(n,t,e,{span(){},point(o,l){o>i&&(r.push({from:i,to:o}),s+=o-i),i=l}},20),i=1)return e[e.length-1].to;let r=Math.floor(t*n);for(let i=0;;i++){let{from:s,to:o}=e[i],l=o-s;if(r<=l)return s+r;r-=l}}function Nf(t,e){let n=0;for(let{from:r,to:i}of t.ranges){if(e<=i){n+=e-r;break}n+=i-r}return n/t.total}function xq(t,e){for(let n of t)if(e(n))return n}const DS={toDOM(t){return t},fromDOM(t){return t},scale:1,eq(t){return t==this}};class D1{constructor(e,n,r){let i=0,s=0,o=0;this.viewports=r.map(({from:l,to:c})=>{let d=n.lineAt(l,sn.ByPos,e,0,0).top,f=n.lineAt(c,sn.ByPos,e,0,0).bottom;return i+=f-d,{from:l,to:c,top:d,bottom:f,domTop:0,domBottom:0}}),this.scale=(7e6-i)/(n.height-i);for(let l of this.viewports)l.domTop=o+(l.top-s)*this.scale,o=l.domBottom=l.domTop+(l.bottom-l.top),s=l.bottom}toDOM(e){for(let n=0,r=0,i=0;;n++){let s=nn.from==e.viewports[r].from&&n.to==e.viewports[r].to):!1}}function Vu(t,e){if(e.scale==1)return t;let n=e.toDOM(t.top),r=e.toDOM(t.bottom);return new Wi(t.from,t.length,n,r-n,Array.isArray(t._content)?t._content.map(i=>Vu(i,e)):t._content)}const Rf=tt.define({combine:t=>t.join(" ")}),$b=tt.define({combine:t=>t.indexOf(!0)>-1}),Wb=wl.newName(),zI=wl.newName(),jI=wl.newName(),$I={"&light":"."+zI,"&dark":"."+jI};function Vb(t,e,n){return new wl(e,{finish(r){return/&/.test(r)?r.replace(/&\w*/,i=>{if(i=="&")return t;if(!n||!n[i])throw new RangeError(`Unsupported selector: ${i}`);return n[i]}):t+" "+r}})}const vq=Vb("."+Wb,{"&":{position:"relative !important",boxSizing:"border-box","&.cm-focused":{outline:"1px dotted #212121"},display:"flex !important",flexDirection:"column"},".cm-scroller":{display:"flex !important",alignItems:"flex-start !important",fontFamily:"monospace",lineHeight:1.4,height:"100%",overflowX:"auto",position:"relative",zIndex:0,overflowAnchor:"none"},".cm-content":{margin:0,flexGrow:2,flexShrink:0,display:"block",whiteSpace:"pre",wordWrap:"normal",boxSizing:"border-box",minHeight:"100%",padding:"4px 0",outline:"none","&[contenteditable=true]":{WebkitUserModify:"read-write-plaintext-only"}},".cm-lineWrapping":{whiteSpace_fallback:"pre-wrap",whiteSpace:"break-spaces",wordBreak:"break-word",overflowWrap:"anywhere",flexShrink:1},"&light .cm-content":{caretColor:"black"},"&dark .cm-content":{caretColor:"white"},".cm-line":{display:"block",padding:"0 2px 0 6px"},".cm-layer":{position:"absolute",left:0,top:0,contain:"size style","& > *":{position:"absolute"}},"&light .cm-selectionBackground":{background:"#d9d9d9"},"&dark .cm-selectionBackground":{background:"#222"},"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#d7d4f0"},"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#233"},".cm-cursorLayer":{pointerEvents:"none"},"&.cm-focused > .cm-scroller > .cm-cursorLayer":{animation:"steps(1) cm-blink 1.2s infinite"},"@keyframes cm-blink":{"0%":{},"50%":{opacity:0},"100%":{}},"@keyframes cm-blink2":{"0%":{},"50%":{opacity:0},"100%":{}},".cm-cursor, .cm-dropCursor":{borderLeft:"1.2px solid black",marginLeft:"-0.6px",pointerEvents:"none"},".cm-cursor":{display:"none"},"&dark .cm-cursor":{borderLeftColor:"#ddd"},".cm-dropCursor":{position:"absolute"},"&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor":{display:"block"},".cm-iso":{unicodeBidi:"isolate"},".cm-announced":{position:"fixed",top:"-10000px"},"@media print":{".cm-announced":{display:"none"}},"&light .cm-activeLine":{backgroundColor:"#cceeff44"},"&dark .cm-activeLine":{backgroundColor:"#99eeff33"},"&light .cm-specialChar":{color:"red"},"&dark .cm-specialChar":{color:"#f78"},".cm-gutters":{flexShrink:0,display:"flex",height:"100%",boxSizing:"border-box",zIndex:200},".cm-gutters-before":{insetInlineStart:0},".cm-gutters-after":{insetInlineEnd:0},"&light .cm-gutters":{backgroundColor:"#f5f5f5",color:"#6c6c6c",border:"0px solid #ddd","&.cm-gutters-before":{borderRightWidth:"1px"},"&.cm-gutters-after":{borderLeftWidth:"1px"}},"&dark .cm-gutters":{backgroundColor:"#333338",color:"#ccc"},".cm-gutter":{display:"flex !important",flexDirection:"column",flexShrink:0,boxSizing:"border-box",minHeight:"100%",overflow:"hidden"},".cm-gutterElement":{boxSizing:"border-box"},".cm-lineNumbers .cm-gutterElement":{padding:"0 3px 0 5px",minWidth:"20px",textAlign:"right",whiteSpace:"nowrap"},"&light .cm-activeLineGutter":{backgroundColor:"#e2f2ff"},"&dark .cm-activeLineGutter":{backgroundColor:"#222227"},".cm-panels":{boxSizing:"border-box",position:"sticky",left:0,right:0,zIndex:300},"&light .cm-panels":{backgroundColor:"#f5f5f5",color:"black"},"&light .cm-panels-top":{borderBottom:"1px solid #ddd"},"&light .cm-panels-bottom":{borderTop:"1px solid #ddd"},"&dark .cm-panels":{backgroundColor:"#333338",color:"white"},".cm-dialog":{padding:"2px 19px 4px 6px",position:"relative","& label":{fontSize:"80%"}},".cm-dialog-close":{position:"absolute",top:"3px",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",fontSize:"14px",padding:"0"},".cm-tab":{display:"inline-block",overflow:"hidden",verticalAlign:"bottom"},".cm-widgetBuffer":{verticalAlign:"text-top",height:"1em",width:0,display:"inline"},".cm-placeholder":{color:"#888",display:"inline-block",verticalAlign:"top",userSelect:"none"},".cm-highlightSpace":{backgroundImage:"radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)",backgroundPosition:"center"},".cm-highlightTab":{backgroundImage:`url('data:image/svg+xml,')`,backgroundSize:"auto 100%",backgroundPosition:"right 90%",backgroundRepeat:"no-repeat"},".cm-trailingSpace":{backgroundColor:"#ff332255"},".cm-button":{verticalAlign:"middle",color:"inherit",fontSize:"70%",padding:".2em 1em",borderRadius:"1px"},"&light .cm-button":{backgroundImage:"linear-gradient(#eff1f5, #d9d9df)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#b4b4b4, #d0d3d6)"}},"&dark .cm-button":{backgroundImage:"linear-gradient(#393939, #111)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#111, #333)"}},".cm-textfield":{verticalAlign:"middle",color:"inherit",fontSize:"70%",border:"1px solid silver",padding:".2em .5em"},"&light .cm-textfield":{backgroundColor:"white"},"&dark .cm-textfield":{border:"1px solid #555",backgroundColor:"inherit"}},$I),wq={childList:!0,characterData:!0,subtree:!0,attributes:!0,characterDataOldValue:!0},x0=Fe.ie&&Fe.ie_version<=11;class Tq{constructor(e){this.view=e,this.active=!1,this.editContext=null,this.selectionRange=new nY,this.selectionChanged=!1,this.delayedFlush=-1,this.resizeTimeout=-1,this.queue=[],this.delayedAndroidKey=null,this.flushingAndroidKey=-1,this.lastChange=0,this.scrollTargets=[],this.intersection=null,this.resizeScroll=null,this.intersecting=!1,this.gapIntersection=null,this.gaps=[],this.printQuery=null,this.parentCheck=-1,this.dom=e.contentDOM,this.observer=new MutationObserver(n=>{for(let r of n)this.queue.push(r);(Fe.ie&&Fe.ie_version<=11||Fe.ios&&e.composing)&&n.some(r=>r.type=="childList"&&r.removedNodes.length||r.type=="characterData"&&r.oldValue.length>r.target.nodeValue.length)?this.flushSoon():this.flush()}),window.EditContext&&Fe.android&&e.constructor.EDIT_CONTEXT!==!1&&!(Fe.chrome&&Fe.chrome_version<126)&&(this.editContext=new _q(e),e.state.facet(ms)&&(e.contentDOM.editContext=this.editContext.editContext)),x0&&(this.onCharData=n=>{this.queue.push({target:n.target,type:"characterData",oldValue:n.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.onResize=this.onResize.bind(this),this.onPrint=this.onPrint.bind(this),this.onScroll=this.onScroll.bind(this),window.matchMedia&&(this.printQuery=window.matchMedia("print")),typeof ResizeObserver=="function"&&(this.resizeScroll=new ResizeObserver(()=>{var n;((n=this.view.docView)===null||n===void 0?void 0:n.lastUpdate){this.parentCheck<0&&(this.parentCheck=setTimeout(this.listenForScroll.bind(this),1e3)),n.length>0&&n[n.length-1].intersectionRatio>0!=this.intersecting&&(this.intersecting=!this.intersecting,this.intersecting!=this.view.inView&&this.onScrollChanged(document.createEvent("Event")))},{threshold:[0,.001]}),this.intersection.observe(this.dom),this.gapIntersection=new IntersectionObserver(n=>{n.length>0&&n[n.length-1].intersectionRatio>0&&this.onScrollChanged(document.createEvent("Event"))},{})),this.listenForScroll(),this.readSelectionRange()}onScrollChanged(e){this.view.inputState.runHandlers("scroll",e),this.intersecting&&this.view.measure()}onScroll(e){this.intersecting&&this.flush(!1),this.editContext&&this.view.requestMeasure(this.editContext.measureReq),this.onScrollChanged(e)}onResize(){this.resizeTimeout<0&&(this.resizeTimeout=setTimeout(()=>{this.resizeTimeout=-1,this.view.requestMeasure()},50))}onPrint(e){(e.type=="change"||!e.type)&&!e.matches||(this.view.viewState.printing=!0,this.view.measure(),setTimeout(()=>{this.view.viewState.printing=!1,this.view.requestMeasure()},500))}updateGaps(e){if(this.gapIntersection&&(e.length!=this.gaps.length||this.gaps.some((n,r)=>n!=e[r]))){this.gapIntersection.disconnect();for(let n of e)this.gapIntersection.observe(n);this.gaps=e}}onSelectionChange(e){let n=this.selectionChanged;if(!this.readSelectionRange()||this.delayedAndroidKey)return;let{view:r}=this,i=this.selectionRange;if(r.state.facet(ms)?r.root.activeElement!=this.dom:!Wf(this.dom,i))return;let s=i.anchorNode&&r.docView.nearest(i.anchorNode);if(s&&s.ignoreEvent(e)){n||(this.selectionChanged=!1);return}(Fe.ie&&Fe.ie_version<=11||Fe.android&&Fe.chrome)&&!r.state.selection.main.empty&&i.focusNode&&nc(i.focusNode,i.focusOffset,i.anchorNode,i.anchorOffset)?this.flushSoon():this.flush(!1)}readSelectionRange(){let{view:e}=this,n=Tc(e.root);if(!n)return!1;let r=Fe.safari&&e.root.nodeType==11&&e.root.activeElement==this.dom&&Sq(this.view,n)||n;if(!r||this.selectionRange.eq(r))return!1;let i=Wf(this.dom,r);return i&&!this.selectionChanged&&e.inputState.lastFocusTime>Date.now()-200&&e.inputState.lastTouchTime{let s=this.delayedAndroidKey;s&&(this.clearDelayedAndroidKey(),this.view.inputState.lastKeyCode=s.keyCode,this.view.inputState.lastKeyTime=Date.now(),!this.flush()&&s.force&&cl(this.dom,s.key,s.keyCode))};this.flushingAndroidKey=this.view.win.requestAnimationFrame(i)}(!this.delayedAndroidKey||e=="Enter")&&(this.delayedAndroidKey={key:e,keyCode:n,force:this.lastChange{this.delayedFlush=-1,this.flush()}))}forceFlush(){this.delayedFlush>=0&&(this.view.win.cancelAnimationFrame(this.delayedFlush),this.delayedFlush=-1),this.flush()}pendingRecords(){for(let e of this.observer.takeRecords())this.queue.push(e);return this.queue}processRecords(){let e=this.pendingRecords();e.length&&(this.queue=[]);let n=-1,r=-1,i=!1;for(let s of e){let o=this.readMutation(s);o&&(o.typeOver&&(i=!0),n==-1?{from:n,to:r}=o:(n=Math.min(o.from,n),r=Math.max(o.to,r)))}return{from:n,to:r,typeOver:i}}readChange(){let{from:e,to:n,typeOver:r}=this.processRecords(),i=this.selectionChanged&&Wf(this.dom,this.selectionRange);if(e<0&&!i)return null;e>-1&&(this.lastChange=Date.now()),this.view.inputState.lastFocusTime=0,this.selectionChanged=!1;let s=new zY(this.view,e,n,r);return this.view.docView.domChanged={newSel:s.newSel?s.newSel.main:null},s}flush(e=!0){if(this.delayedFlush>=0||this.delayedAndroidKey)return!1;e&&this.readSelectionRange();let n=this.readChange();if(!n)return this.view.requestMeasure(),!1;let r=this.view.state,i=II(this.view,n);return this.view.state==r&&(n.domChanged||n.newSel&&!n.newSel.main.eq(this.view.state.selection.main))&&this.view.update([]),i}readMutation(e){let n=this.view.docView.nearest(e.target);if(!n||n.ignoreMutation(e))return null;if(n.markDirty(e.type=="attributes"),e.type=="attributes"&&(n.flags|=4),e.type=="childList"){let r=LS(n,e.previousSibling||e.target.previousSibling,-1),i=LS(n,e.nextSibling||e.target.nextSibling,1);return{from:r?n.posAfter(r):n.posAtStart,to:i?n.posBefore(i):n.posAtEnd,typeOver:!1}}else return e.type=="characterData"?{from:n.posAtStart,to:n.posAtEnd,typeOver:e.target.nodeValue==e.oldValue}:null}setWindow(e){e!=this.win&&(this.removeWindowListeners(this.win),this.win=e,this.addWindowListeners(this.win))}addWindowListeners(e){e.addEventListener("resize",this.onResize),this.printQuery?this.printQuery.addEventListener?this.printQuery.addEventListener("change",this.onPrint):this.printQuery.addListener(this.onPrint):e.addEventListener("beforeprint",this.onPrint),e.addEventListener("scroll",this.onScroll),e.document.addEventListener("selectionchange",this.onSelectionChange)}removeWindowListeners(e){e.removeEventListener("scroll",this.onScroll),e.removeEventListener("resize",this.onResize),this.printQuery?this.printQuery.removeEventListener?this.printQuery.removeEventListener("change",this.onPrint):this.printQuery.removeListener(this.onPrint):e.removeEventListener("beforeprint",this.onPrint),e.document.removeEventListener("selectionchange",this.onSelectionChange)}update(e){this.editContext&&(this.editContext.update(e),e.startState.facet(ms)!=e.state.facet(ms)&&(e.view.contentDOM.editContext=e.state.facet(ms)?this.editContext.editContext:null))}destroy(){var e,n,r;this.stop(),(e=this.intersection)===null||e===void 0||e.disconnect(),(n=this.gapIntersection)===null||n===void 0||n.disconnect(),(r=this.resizeScroll)===null||r===void 0||r.disconnect();for(let i of this.scrollTargets)i.removeEventListener("scroll",this.onScroll);this.removeWindowListeners(this.win),clearTimeout(this.parentCheck),clearTimeout(this.resizeTimeout),this.win.cancelAnimationFrame(this.delayedFlush),this.win.cancelAnimationFrame(this.flushingAndroidKey),this.editContext&&(this.view.contentDOM.editContext=null,this.editContext.destroy())}}function LS(t,e,n){for(;e;){let r=Gt.get(e);if(r&&r.parent==t)return r;let i=e.parentNode;e=i!=t.dom?i:n>0?e.nextSibling:e.previousSibling}return null}function PS(t,e){let n=e.startContainer,r=e.startOffset,i=e.endContainer,s=e.endOffset,o=t.docView.domAtPos(t.state.selection.main.anchor);return nc(o.node,o.offset,i,s)&&([n,r,i,s]=[i,s,n,r]),{anchorNode:n,anchorOffset:r,focusNode:i,focusOffset:s}}function Sq(t,e){if(e.getComposedRanges){let i=e.getComposedRanges(t.root)[0];if(i)return PS(t,i)}let n=null;function r(i){i.preventDefault(),i.stopImmediatePropagation(),n=i.getTargetRanges()[0]}return t.contentDOM.addEventListener("beforeinput",r,!0),t.dom.ownerDocument.execCommand("indent"),t.contentDOM.removeEventListener("beforeinput",r,!0),n?PS(t,n):null}class _q{constructor(e){this.from=0,this.to=0,this.pendingContextChange=null,this.handlers=Object.create(null),this.composing=null,this.resetRange(e.state);let n=this.editContext=new window.EditContext({text:e.state.doc.sliceString(this.from,this.to),selectionStart:this.toContextPos(Math.max(this.from,Math.min(this.to,e.state.selection.main.anchor))),selectionEnd:this.toContextPos(e.state.selection.main.head)});this.handlers.textupdate=r=>{let i=e.state.selection.main,{anchor:s,head:o}=i,l=this.toEditorPos(r.updateRangeStart),c=this.toEditorPos(r.updateRangeEnd);e.inputState.composing>=0&&!this.composing&&(this.composing={contextBase:r.updateRangeStart,editorBase:l,drifted:!1});let d={from:l,to:c,insert:Lt.of(r.text.split(` `))};if(d.from==this.from&&sthis.to&&(d.to=s),d.from==d.to&&!d.insert.length){let f=Pe.single(this.toEditorPos(r.selectionStart),this.toEditorPos(r.selectionEnd));f.main.eq(i)||e.dispatch({selection:f,userEvent:"select"});return}if((Fe.mac||Fe.android)&&d.from==o-1&&/^\. ?$/.test(r.text)&&e.contentDOM.getAttribute("autocorrect")=="off"&&(d={from:l,to:c,insert:Lt.of([r.text.replace("."," ")])}),this.pendingContextChange=d,!e.state.readOnly){let f=this.to-this.from+(d.to-d.from+d.insert.length);O1(e,d,Pe.single(this.toEditorPos(r.selectionStart,f),this.toEditorPos(r.selectionEnd,f)))}this.pendingContextChange&&(this.revertPending(e.state),this.setSelection(e.state))},this.handlers.characterboundsupdate=r=>{let i=[],s=null;for(let o=this.toEditorPos(r.rangeStart),l=this.toEditorPos(r.rangeEnd);o{let i=[];for(let s of r.getTextFormats()){let o=s.underlineStyle,l=s.underlineThickness;if(o!="None"&&l!="None"){let c=this.toEditorPos(s.rangeStart),d=this.toEditorPos(s.rangeEnd);if(c{e.inputState.composing<0&&(e.inputState.composing=0,e.inputState.compositionFirstChange=!0)},this.handlers.compositionend=()=>{if(e.inputState.composing=-1,e.inputState.compositionFirstChange=null,this.composing){let{drifted:r}=this.composing;this.composing=null,r&&this.reset(e.state)}};for(let r in this.handlers)n.addEventListener(r,this.handlers[r]);this.measureReq={read:r=>{this.editContext.updateControlBounds(r.contentDOM.getBoundingClientRect());let i=Tc(r.root);i&&i.rangeCount&&this.editContext.updateSelectionBounds(i.getRangeAt(0).getBoundingClientRect())}}}applyEdits(e){let n=0,r=!1,i=this.pendingContextChange;return e.changes.iterChanges((s,o,l,c,d)=>{if(r)return;let f=d.length-(o-s);if(i&&o>=i.to)if(i.from==s&&i.to==o&&i.insert.eq(d)){i=this.pendingContextChange=null,n+=f,this.to+=f;return}else i=null,this.revertPending(e.state);if(s+=n,o+=n,o<=this.from)this.from+=f,this.to+=f;else if(sthis.to||this.to-this.from+d.length>3e4){r=!0;return}this.editContext.updateText(this.toContextPos(s),this.toContextPos(o),d.toString()),this.to+=f}n+=f}),i&&!r&&this.revertPending(e.state),!r}update(e){let n=this.pendingContextChange,r=e.startState.selection.main;this.composing&&(this.composing.drifted||!e.changes.touchesRange(r.from,r.to)&&e.transactions.some(i=>!i.isUserEvent("input.type")&&i.changes.touchesRange(this.from,this.to)))?(this.composing.drifted=!0,this.composing.editorBase=e.changes.mapPos(this.composing.editorBase)):!this.applyEdits(e)||!this.rangeIsValid(e.state)?(this.pendingContextChange=null,this.reset(e.state)):(e.docChanged||e.selectionSet||n)&&this.setSelection(e.state),(e.geometryChanged||e.docChanged||e.selectionSet)&&e.view.requestMeasure(this.measureReq)}resetRange(e){let{head:n}=e.selection.main;this.from=Math.max(0,n-1e4),this.to=Math.min(e.doc.length,n+1e4)}reset(e){this.resetRange(e),this.editContext.updateText(0,this.editContext.text.length,e.doc.sliceString(this.from,this.to)),this.setSelection(e)}revertPending(e){let n=this.pendingContextChange;this.pendingContextChange=null,this.editContext.updateText(this.toContextPos(n.from),this.toContextPos(n.from+n.insert.length),e.doc.sliceString(n.from,n.to))}setSelection(e){let{main:n}=e.selection,r=this.toContextPos(Math.max(this.from,Math.min(this.to,n.anchor))),i=this.toContextPos(n.head);(this.editContext.selectionStart!=r||this.editContext.selectionEnd!=i)&&this.editContext.updateSelection(r,i)}rangeIsValid(e){let{head:n}=e.selection.main;return!(this.from>0&&n-this.from<500||this.to1e4*3)}toEditorPos(e,n=this.to-this.from){e=Math.min(e,n);let r=this.composing;return r&&r.drifted?r.editorBase+(e-r.contextBase):e+this.from}toContextPos(e){let n=this.composing;return n&&n.drifted?n.contextBase+(e-n.editorBase):e-this.from}destroy(){for(let e in this.handlers)this.editContext.removeEventListener(e,this.handlers[e])}}class ft{get state(){return this.viewState.state}get viewport(){return this.viewState.viewport}get visibleRanges(){return this.viewState.visibleRanges}get inView(){return this.viewState.inView}get composing(){return!!this.inputState&&this.inputState.composing>0}get compositionStarted(){return!!this.inputState&&this.inputState.composing>=0}get root(){return this._root}get win(){return this.dom.ownerDocument.defaultView||window}constructor(e={}){var n;this.plugins=[],this.pluginMap=new Map,this.editorAttrs={},this.contentAttrs={},this.bidiCache=[],this.destroyed=!1,this.updateState=2,this.measureScheduled=-1,this.measureRequests=[],this.contentDOM=document.createElement("div"),this.scrollDOM=document.createElement("div"),this.scrollDOM.tabIndex=-1,this.scrollDOM.className="cm-scroller",this.scrollDOM.appendChild(this.contentDOM),this.announceDOM=document.createElement("div"),this.announceDOM.className="cm-announced",this.announceDOM.setAttribute("aria-live","polite"),this.dom=document.createElement("div"),this.dom.appendChild(this.announceDOM),this.dom.appendChild(this.scrollDOM),e.parent&&e.parent.appendChild(this.dom);let{dispatch:r}=e;this.dispatchTransactions=e.dispatchTransactions||r&&(i=>i.forEach(s=>r(s,this)))||(i=>this.update(i)),this.dispatch=this.dispatch.bind(this),this._root=e.root||rY(e.parent)||document,this.viewState=new MS(e.state||Wt.create(e)),e.scrollTo&&e.scrollTo.is(Sf)&&(this.viewState.scrollTarget=e.scrollTo.value.clip(this.viewState.state)),this.plugins=this.state.facet(el).map(i=>new g0(i));for(let i of this.plugins)i.update(this);this.observer=new Tq(this),this.inputState=new GY(this),this.inputState.ensureHandlers(this.plugins),this.docView=new pS(this),this.mountStyles(),this.updateAttrs(),this.updateState=0,this.requestMeasure(),!((n=document.fonts)===null||n===void 0)&&n.ready&&document.fonts.ready.then(()=>this.requestMeasure())}dispatch(...e){let n=e.length==1&&e[0]instanceof or?e:e.length==1&&Array.isArray(e[0])?e[0]:[this.state.update(...e)];this.dispatchTransactions(n,this)}update(e){if(this.updateState!=0)throw new Error("Calls to EditorView.update are not allowed while an update is in progress");let n=!1,r=!1,i,s=this.state;for(let m of e){if(m.startState!=s)throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");s=m.state}if(this.destroyed){this.viewState.state=s;return}let o=this.hasFocus,l=0,c=null;e.some(m=>m.annotation(FI))?(this.inputState.notifiedFocused=o,l=1):o!=this.inputState.notifiedFocused&&(this.inputState.notifiedFocused=o,c=BI(s,o),c||(l=1));let d=this.observer.delayedAndroidKey,f=null;if(d?(this.observer.clearDelayedAndroidKey(),f=this.observer.readChange(),(f&&!this.state.doc.eq(s.doc)||!this.state.selection.eq(s.selection))&&(f=null)):this.observer.clear(),s.facet(Wt.phrases)!=this.state.facet(Wt.phrases))return this.setState(s);i=Sh.create(this,s,e),i.flags|=l;let p=this.viewState.scrollTarget;try{this.updateState=2;for(let m of e){if(p&&(p=p.map(m.changes)),m.scrollIntoView){let{main:g}=m.state.selection;p=new dl(g.empty?g:Pe.cursor(g.head,g.head>g.anchor?-1:1))}for(let g of m.effects)g.is(Sf)&&(p=g.value.clip(this.state))}this.viewState.update(i,p),this.bidiCache=Ch.update(this.bidiCache,i.changes),i.empty||(this.updatePlugins(i),this.inputState.update(i)),n=this.docView.update(i),this.state.facet($u)!=this.styleModules&&this.mountStyles(),r=this.updateAttrs(),this.showAnnouncements(e),this.docView.updateSelection(n,e.some(m=>m.isUserEvent("select.pointer")))}finally{this.updateState=0}if(i.startState.facet(Rf)!=i.state.facet(Rf)&&(this.viewState.mustMeasureContent=!0),(n||r||p||this.viewState.mustEnforceCursorAssoc||this.viewState.mustMeasureContent)&&this.requestMeasure(),n&&this.docViewUpdate(),!i.empty)for(let m of this.state.facet(Hb))try{m(i)}catch(g){gs(this.state,g,"update listener")}(c||f)&&Promise.resolve().then(()=>{c&&this.state==c.startState&&this.dispatch(c),f&&!II(this,f)&&d.force&&cl(this.contentDOM,d.key,d.keyCode)})}setState(e){if(this.updateState!=0)throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");if(this.destroyed){this.viewState.state=e;return}this.updateState=2;let n=this.hasFocus;try{for(let r of this.plugins)r.destroy(this);this.viewState=new MS(e),this.plugins=e.facet(el).map(r=>new g0(r)),this.pluginMap.clear();for(let r of this.plugins)r.update(this);this.docView.destroy(),this.docView=new pS(this),this.inputState.ensureHandlers(this.plugins),this.mountStyles(),this.updateAttrs(),this.bidiCache=[]}finally{this.updateState=0}n&&this.focus(),this.requestMeasure()}updatePlugins(e){let n=e.startState.facet(el),r=e.state.facet(el);if(n!=r){let i=[];for(let s of r){let o=n.indexOf(s);if(o<0)i.push(new g0(s));else{let l=this.plugins[o];l.mustUpdate=e,i.push(l)}}for(let s of this.plugins)s.mustUpdate!=e&&s.destroy(this);this.plugins=i,this.pluginMap.clear()}else for(let i of this.plugins)i.mustUpdate=e;for(let i=0;i-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.observer.delayedAndroidKey){this.measureScheduled=-1,this.requestMeasure();return}this.measureScheduled=0,e&&this.observer.forceFlush();let n=null,r=this.scrollDOM,i=r.scrollTop*this.scaleY,{scrollAnchorPos:s,scrollAnchorHeight:o}=this.viewState;Math.abs(i-this.viewState.scrollTop)>1&&(o=-1),this.viewState.scrollAnchorHeight=-1;try{for(let l=0;;l++){if(o<0)if(ZR(r))s=-1,o=this.viewState.heightMap.height;else{let g=this.viewState.scrollAnchorAt(i);s=g.from,o=g.top}this.updateState=1;let c=this.viewState.measure(this);if(!c&&!this.measureRequests.length&&this.viewState.scrollTarget==null)break;if(l>5){console.warn(this.measureRequests.length?"Measure loop restarted more than 5 times":"Viewport failed to stabilize");break}let d=[];c&4||([this.measureRequests,d]=[d,this.measureRequests]);let f=d.map(g=>{try{return g.read(this)}catch(x){return gs(this.state,x),FS}}),p=Sh.create(this,this.state,[]),m=!1;p.flags|=c,n?n.flags|=c:n=p,this.updateState=2,p.empty||(this.updatePlugins(p),this.inputState.update(p),this.updateAttrs(),m=this.docView.update(p),m&&this.docViewUpdate());for(let g=0;g1||x<-1){i=i+x,r.scrollTop=i/this.scaleY,o=-1;continue}}break}}}finally{this.updateState=0,this.measureScheduled=-1}if(n&&!n.empty)for(let l of this.state.facet(Hb))l(n)}get themeClasses(){return Wb+" "+(this.state.facet($b)?jI:zI)+" "+this.state.facet(Rf)}updateAttrs(){let e=BS(this,SI,{class:"cm-editor"+(this.hasFocus?" cm-focused ":" ")+this.themeClasses}),n={spellcheck:"false",autocorrect:"off",autocapitalize:"off",writingsuggestions:"false",translate:"no",contenteditable:this.state.facet(ms)?"true":"false",class:"cm-content",style:`${Fe.tabSize}: ${this.state.tabSize}`,role:"textbox","aria-multiline":"true"};this.state.readOnly&&(n["aria-readonly"]="true"),BS(this,R1,n);let r=this.observer.ignore(()=>{let i=Lb(this.contentDOM,this.contentAttrs,n),s=Lb(this.dom,this.editorAttrs,e);return i||s});return this.editorAttrs=e,this.contentAttrs=n,r}showAnnouncements(e){let n=!0;for(let r of e)for(let i of r.effects)if(i.is(ft.announce)){n&&(this.announceDOM.textContent=""),n=!1;let s=this.announceDOM.appendChild(document.createElement("div"));s.textContent=i.value}}mountStyles(){this.styleModules=this.state.facet($u);let e=this.state.facet(ft.cspNonce);wl.mount(this.root,this.styleModules.concat(vq).reverse(),e?{nonce:e}:void 0)}readMeasured(){if(this.updateState==2)throw new Error("Reading the editor layout isn't allowed during an update");this.updateState==0&&this.measureScheduled>-1&&this.measure(!1)}requestMeasure(e){if(this.measureScheduled<0&&(this.measureScheduled=this.win.requestAnimationFrame(()=>this.measure())),e){if(this.measureRequests.indexOf(e)>-1)return;if(e.key!=null){for(let n=0;nr.plugin==e)||null),n&&n.update(this).value}get documentTop(){return this.contentDOM.getBoundingClientRect().top+this.viewState.paddingTop}get documentPadding(){return{top:this.viewState.paddingTop,bottom:this.viewState.paddingBottom}}get scaleX(){return this.viewState.scaleX}get scaleY(){return this.viewState.scaleY}elementAtHeight(e){return this.readMeasured(),this.viewState.elementAtHeight(e)}lineBlockAtHeight(e){return this.readMeasured(),this.viewState.lineBlockAtHeight(e)}get viewportLineBlocks(){return this.viewState.viewportLines}lineBlockAt(e){return this.viewState.lineBlockAt(e)}get contentHeight(){return this.viewState.contentHeight}moveByChar(e,n,r){return E0(this,e,yS(this,e,n,r))}moveByGroup(e,n){return E0(this,e,yS(this,e,n,r=>FY(this,e.head,r)))}visualLineSide(e,n){let r=this.bidiSpans(e),i=this.textDirectionAt(e.from),s=r[n?r.length-1:0];return Pe.cursor(s.side(n,i)+e.from,s.forward(!n,i)?1:-1)}moveToLineBoundary(e,n,r=!0){return PY(this,e,n,r)}moveVertically(e,n,r){return E0(this,e,BY(this,e,n,r))}domAtPos(e){return this.docView.domAtPos(e)}posAtDOM(e,n=0){return this.docView.posFromDOM(e,n)}posAtCoords(e,n=!0){return this.readMeasured(),RI(this,e,n)}coordsAtPos(e,n=1){this.readMeasured();let r=this.docView.coordsAt(e,n);if(!r||r.left==r.right)return r;let i=this.state.doc.lineAt(e),s=this.bidiSpans(i),o=s[so.find(s,e-i.from,-1,n)];return _p(r,o.dir==ar.LTR==n>0)}coordsForChar(e){return this.readMeasured(),this.docView.coordsForChar(e)}get defaultCharacterWidth(){return this.viewState.heightOracle.charWidth}get defaultLineHeight(){return this.viewState.heightOracle.lineHeight}get textDirection(){return this.viewState.defaultTextDirection}textDirectionAt(e){return!this.state.facet(vI)||ethis.viewport.to?this.textDirection:(this.readMeasured(),this.docView.textDirectionAt(e))}get lineWrapping(){return this.viewState.heightOracle.lineWrapping}bidiSpans(e){if(e.length>Cq)return hI(e.length);let n=this.textDirectionAt(e.from),r;for(let s of this.bidiCache)if(s.from==e.from&&s.dir==n&&(s.fresh||fI(s.isolates,r=hS(this,e))))return s.order;r||(r=hS(this,e));let i=EY(e.text,n,r);return this.bidiCache.push(new Ch(e.from,e.to,n,r,!0,i)),i}get hasFocus(){var e;return(this.dom.ownerDocument.hasFocus()||Fe.safari&&((e=this.inputState)===null||e===void 0?void 0:e.lastContextMenu)>Date.now()-3e4)&&this.root.activeElement==this.contentDOM}focus(){this.observer.ignore(()=>{XR(this.contentDOM),this.docView.updateSelection()})}setRoot(e){this._root!=e&&(this._root=e,this.observer.setWindow((e.nodeType==9?e:e.ownerDocument).defaultView||window),this.mountStyles())}destroy(){this.root.activeElement==this.contentDOM&&this.contentDOM.blur();for(let e of this.plugins)e.destroy(this);this.plugins=[],this.inputState.destroy(),this.docView.destroy(),this.dom.remove(),this.observer.destroy(),this.measureScheduled>-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.destroyed=!0}static scrollIntoView(e,n={}){return Sf.of(new dl(typeof e=="number"?Pe.cursor(e):e,n.y,n.x,n.yMargin,n.xMargin))}scrollSnapshot(){let{scrollTop:e,scrollLeft:n}=this.scrollDOM,r=this.viewState.scrollAnchorAt(e);return Sf.of(new dl(Pe.cursor(r.from),"start","start",r.top-e,n,!0))}setTabFocusMode(e){e==null?this.inputState.tabFocusMode=this.inputState.tabFocusMode<0?0:-1:typeof e=="boolean"?this.inputState.tabFocusMode=e?0:-1:this.inputState.tabFocusMode!=0&&(this.inputState.tabFocusMode=Date.now()+e)}static domEventHandlers(e){return Ss.define(()=>({}),{eventHandlers:e})}static domEventObservers(e){return Ss.define(()=>({}),{eventObservers:e})}static theme(e,n){let r=wl.newName(),i=[Rf.of(r),$u.of(Vb(`.${r}`,e))];return n&&n.dark&&i.push($b.of(!0)),i}static baseTheme(e){return w1.lowest($u.of(Vb("."+Wb,e,$I)))}static findFromDOM(e){var n;let r=e.querySelector(".cm-content"),i=r&&Gt.get(r)||Gt.get(e);return((n=i?.rootView)===null||n===void 0?void 0:n.view)||null}}ft.styleModule=$u;ft.inputHandler=yI;ft.clipboardInputFilter=k1;ft.clipboardOutputFilter=N1;ft.scrollHandler=wI;ft.focusChangeEffect=xI;ft.perLineTextDirection=vI;ft.exceptionSink=EI;ft.updateListener=Hb;ft.editable=ms;ft.mouseSelectionStyle=bI;ft.dragMovesSelection=gI;ft.clickAddsSelectionRange=mI;ft.decorations=_c;ft.outerDecorations=_I;ft.atomicRanges=I1;ft.bidiIsolatedRanges=CI;ft.scrollMargins=AI;ft.darkTheme=$b;ft.cspNonce=tt.define({combine:t=>t.length?t[0]:""});ft.contentAttributes=R1;ft.editorAttributes=SI;ft.lineWrapping=ft.contentAttributes.of({class:"cm-lineWrapping"});ft.announce=bn.define();const Cq=4096,FS={};class Ch{constructor(e,n,r,i,s,o){this.from=e,this.to=n,this.dir=r,this.isolates=i,this.fresh=s,this.order=o}static update(e,n){if(n.empty&&!e.some(s=>s.fresh))return e;let r=[],i=e.length?e[e.length-1].dir:ar.LTR;for(let s=Math.max(0,e.length-10);s=0;i--){let s=r[i],o=typeof s=="function"?s(t):s;o&&Db(o,n)}return n}const Aq=Fe.mac?"mac":Fe.windows?"win":Fe.linux?"linux":"key";function kq(t,e){const n=t.split(/-(?!$)/);let r=n[n.length-1];r=="Space"&&(r=" ");let i,s,o,l;for(let c=0;cr.concat(i),[]))),n}function Rq(t,e,n){return GI(VI(t.state),e,t,n)}let no=null;const Iq=4e3;function Oq(t,e=Aq){let n=Object.create(null),r=Object.create(null),i=(o,l)=>{let c=r[o];if(c==null)r[o]=l;else if(c!=l)throw new Error("Key binding "+o+" is used both as a regular binding and as a multi-stroke prefix")},s=(o,l,c,d,f)=>{var p,m;let g=n[o]||(n[o]=Object.create(null)),x=l.split(/ (?!$)/).map(C=>kq(C,e));for(let C=1;C{let M=no={view:k,prefix:A,scope:o};return setTimeout(()=>{no==M&&(no=null)},Iq),!0}]})}let v=x.join(" ");i(v,!1);let S=g[v]||(g[v]={preventDefault:!1,stopPropagation:!1,run:((m=(p=g._any)===null||p===void 0?void 0:p.run)===null||m===void 0?void 0:m.slice())||[]});c&&S.run.push(c),d&&(S.preventDefault=!0),f&&(S.stopPropagation=!0)};for(let o of t){let l=o.scope?o.scope.split(" "):["editor"];if(o.any)for(let d of l){let f=n[d]||(n[d]=Object.create(null));f._any||(f._any={preventDefault:!1,stopPropagation:!1,run:[]});let{any:p}=o;for(let m in f)f[m].run.push(g=>p(g,Gb))}let c=o[e]||o.key;if(c)for(let d of l)s(d,c,o.run,o.preventDefault,o.stopPropagation),o.shift&&s(d,"Shift-"+c,o.shift,o.preventDefault,o.stopPropagation)}return n}let Gb=null;function GI(t,e,n,r){Gb=e;let i=ZK(e),s=DR(i,0),o=LR(s)==i.length&&i!=" ",l="",c=!1,d=!1,f=!1;no&&no.view==n&&no.scope==r&&(l=no.prefix+" ",MI.indexOf(e.keyCode)<0&&(d=!0,no=null));let p=new Set,m=S=>{if(S){for(let C of S.run)if(!p.has(C)&&(p.add(C),C(n)))return S.stopPropagation&&(f=!0),!0;S.preventDefault&&(S.stopPropagation&&(f=!0),d=!0)}return!1},g=t[r],x,v;return g&&(m(g[l+If(i,e,!o)])?c=!0:o&&(e.altKey||e.metaKey||e.ctrlKey)&&!(Fe.windows&&e.ctrlKey&&e.altKey)&&!(Fe.mac&&e.altKey&&!e.ctrlKey)&&(x=fo[e.keyCode])&&x!=i?(m(g[l+If(x,e,!0)])||e.shiftKey&&(v=wc[e.keyCode])!=i&&v!=x&&m(g[l+If(v,e,!1)]))&&(c=!0):o&&e.shiftKey&&m(g[l+If(i,e,!0)])&&(c=!0),!c&&m(g._any)&&(c=!0)),d&&(c=!0),c&&f&&e.stopPropagation(),Gb=null,c}const HS=tt.define({combine(t){let e,n;for(let r of t)e=e||r.topContainer,n=n||r.bottomContainer;return{topContainer:e,bottomContainer:n}}});function Ah(t,e){let n=t.plugin(KI),r=n?n.specs.indexOf(e):-1;return r>-1?n.panels[r]:null}const KI=Ss.fromClass(class{constructor(t){this.input=t.state.facet(kh),this.specs=this.input.filter(n=>n),this.panels=this.specs.map(n=>n(t));let e=t.state.facet(HS);this.top=new Of(t,!0,e.topContainer),this.bottom=new Of(t,!1,e.bottomContainer),this.top.sync(this.panels.filter(n=>n.top)),this.bottom.sync(this.panels.filter(n=>!n.top));for(let n of this.panels)n.dom.classList.add("cm-panel"),n.mount&&n.mount()}update(t){let e=t.state.facet(HS);this.top.container!=e.topContainer&&(this.top.sync([]),this.top=new Of(t.view,!0,e.topContainer)),this.bottom.container!=e.bottomContainer&&(this.bottom.sync([]),this.bottom=new Of(t.view,!1,e.bottomContainer)),this.top.syncClasses(),this.bottom.syncClasses();let n=t.state.facet(kh);if(n!=this.input){let r=n.filter(c=>c),i=[],s=[],o=[],l=[];for(let c of r){let d=this.specs.indexOf(c),f;d<0?(f=c(t.view),l.push(f)):(f=this.panels[d],f.update&&f.update(t)),i.push(f),(f.top?s:o).push(f)}this.specs=r,this.panels=i,this.top.sync(s),this.bottom.sync(o);for(let c of l)c.dom.classList.add("cm-panel"),c.mount&&c.mount()}else for(let r of this.panels)r.update&&r.update(t)}destroy(){this.top.sync([]),this.bottom.sync([])}},{provide:t=>ft.scrollMargins.of(e=>{let n=e.plugin(t);return n&&{top:n.top.scrollMargin(),bottom:n.bottom.scrollMargin()}})});class Of{constructor(e,n,r){this.view=e,this.top=n,this.container=r,this.dom=void 0,this.classes="",this.panels=[],this.syncClasses()}sync(e){for(let n of this.panels)n.destroy&&e.indexOf(n)<0&&n.destroy();this.panels=e,this.syncDOM()}syncDOM(){if(this.panels.length==0){this.dom&&(this.dom.remove(),this.dom=void 0);return}if(!this.dom){this.dom=document.createElement("div"),this.dom.className=this.top?"cm-panels cm-panels-top":"cm-panels cm-panels-bottom",this.dom.style[this.top?"top":"bottom"]="0";let n=this.container||this.view.dom;n.insertBefore(this.dom,this.top?n.firstChild:null)}let e=this.dom.firstChild;for(let n of this.panels)if(n.dom.parentNode==this.dom){for(;e!=n.dom;)e=zS(e);e=e.nextSibling}else this.dom.insertBefore(n.dom,e);for(;e;)e=zS(e)}scrollMargin(){return!this.dom||this.container?0:Math.max(0,this.top?this.dom.getBoundingClientRect().bottom-Math.max(0,this.view.scrollDOM.getBoundingClientRect().top):Math.min(innerHeight,this.view.scrollDOM.getBoundingClientRect().bottom)-this.dom.getBoundingClientRect().top)}syncClasses(){if(!(!this.container||this.classes==this.view.themeClasses)){for(let e of this.classes.split(" "))e&&this.container.classList.remove(e);for(let e of(this.classes=this.view.themeClasses).split(" "))e&&this.container.classList.add(e)}}}function zS(t){let e=t.nextSibling;return t.remove(),e}const kh=tt.define({enables:KI});class ra extends vl{compare(e){return this==e||this.constructor==e.constructor&&this.eq(e)}eq(e){return!1}destroy(e){}}ra.prototype.elementClass="";ra.prototype.toDOM=void 0;ra.prototype.mapMode=Wr.TrackBefore;ra.prototype.startSide=ra.prototype.endSide=-1;ra.prototype.point=!0;const v0=tt.define(),Mq=tt.define(),Yf=tt.define(),jS=tt.define({combine:t=>t.some(e=>e)});function Dq(t){return[Lq]}const Lq=Ss.fromClass(class{constructor(t){this.view=t,this.domAfter=null,this.prevViewport=t.viewport,this.dom=document.createElement("div"),this.dom.className="cm-gutters cm-gutters-before",this.dom.setAttribute("aria-hidden","true"),this.dom.style.minHeight=this.view.contentHeight/this.view.scaleY+"px",this.gutters=t.state.facet(Yf).map(e=>new WS(t,e)),this.fixed=!t.state.facet(jS);for(let e of this.gutters)e.config.side=="after"?this.getDOMAfter().appendChild(e.dom):this.dom.appendChild(e.dom);this.fixed&&(this.dom.style.position="sticky"),this.syncGutters(!1),t.scrollDOM.insertBefore(this.dom,t.contentDOM)}getDOMAfter(){return this.domAfter||(this.domAfter=document.createElement("div"),this.domAfter.className="cm-gutters cm-gutters-after",this.domAfter.setAttribute("aria-hidden","true"),this.domAfter.style.minHeight=this.view.contentHeight/this.view.scaleY+"px",this.domAfter.style.position=this.fixed?"sticky":"",this.view.scrollDOM.appendChild(this.domAfter)),this.domAfter}update(t){if(this.updateGutters(t)){let e=this.prevViewport,n=t.view.viewport,r=Math.min(e.to,n.to)-Math.max(e.from,n.from);this.syncGutters(r<(n.to-n.from)*.8)}if(t.geometryChanged){let e=this.view.contentHeight/this.view.scaleY+"px";this.dom.style.minHeight=e,this.domAfter&&(this.domAfter.style.minHeight=e)}this.view.state.facet(jS)!=!this.fixed&&(this.fixed=!this.fixed,this.dom.style.position=this.fixed?"sticky":"",this.domAfter&&(this.domAfter.style.position=this.fixed?"sticky":"")),this.prevViewport=t.view.viewport}syncGutters(t){let e=this.dom.nextSibling;t&&(this.dom.remove(),this.domAfter&&this.domAfter.remove());let n=Ht.iter(this.view.state.facet(v0),this.view.viewport.from),r=[],i=this.gutters.map(s=>new Pq(s,this.view.viewport,-this.view.documentPadding.top));for(let s of this.view.viewportLineBlocks)if(r.length&&(r=[]),Array.isArray(s.type)){let o=!0;for(let l of s.type)if(l.type==ii.Text&&o){Kb(n,r,l.from);for(let c of i)c.line(this.view,l,r);o=!1}else if(l.widget)for(let c of i)c.widget(this.view,l)}else if(s.type==ii.Text){Kb(n,r,s.from);for(let o of i)o.line(this.view,s,r)}else if(s.widget)for(let o of i)o.widget(this.view,s);for(let s of i)s.finish();t&&(this.view.scrollDOM.insertBefore(this.dom,e),this.domAfter&&this.view.scrollDOM.appendChild(this.domAfter))}updateGutters(t){let e=t.startState.facet(Yf),n=t.state.facet(Yf),r=t.docChanged||t.heightChanged||t.viewportChanged||!Ht.eq(t.startState.facet(v0),t.state.facet(v0),t.view.viewport.from,t.view.viewport.to);if(e==n)for(let i of this.gutters)i.update(t)&&(r=!0);else{r=!0;let i=[];for(let s of n){let o=e.indexOf(s);o<0?i.push(new WS(this.view,s)):(this.gutters[o].update(t),i.push(this.gutters[o]))}for(let s of this.gutters)s.dom.remove(),i.indexOf(s)<0&&s.destroy();for(let s of i)s.config.side=="after"?this.getDOMAfter().appendChild(s.dom):this.dom.appendChild(s.dom);this.gutters=i}return r}destroy(){for(let t of this.gutters)t.destroy();this.dom.remove(),this.domAfter&&this.domAfter.remove()}},{provide:t=>ft.scrollMargins.of(e=>{let n=e.plugin(t);if(!n||n.gutters.length==0||!n.fixed)return null;let r=n.dom.offsetWidth*e.scaleX,i=n.domAfter?n.domAfter.offsetWidth*e.scaleX:0;return e.textDirection==ar.LTR?{left:r,right:i}:{right:r,left:i}})});function $S(t){return Array.isArray(t)?t:[t]}function Kb(t,e,n){for(;t.value&&t.from<=n;)t.from==n&&e.push(t.value),t.next()}class Pq{constructor(e,n,r){this.gutter=e,this.height=r,this.i=0,this.cursor=Ht.iter(e.markers,n.from)}addElement(e,n,r){let{gutter:i}=this,s=(n.top-this.height)/e.scaleY,o=n.height/e.scaleY;if(this.i==i.elements.length){let l=new YI(e,o,s,r);i.elements.push(l),i.dom.appendChild(l.dom)}else i.elements[this.i].update(e,o,s,r);this.height=n.bottom,this.i++}line(e,n,r){let i=[];Kb(this.cursor,i,n.from),r.length&&(i=i.concat(r));let s=this.gutter.config.lineMarker(e,n,i);s&&i.unshift(s);let o=this.gutter;i.length==0&&!o.config.renderEmptyElements||this.addElement(e,n,i)}widget(e,n){let r=this.gutter.config.widgetMarker(e,n.widget,n),i=r?[r]:null;for(let s of e.state.facet(Mq)){let o=s(e,n.widget,n);o&&(i||(i=[])).push(o)}i&&this.addElement(e,n,i)}finish(){let e=this.gutter;for(;e.elements.length>this.i;){let n=e.elements.pop();e.dom.removeChild(n.dom),n.destroy()}}}class WS{constructor(e,n){this.view=e,this.config=n,this.elements=[],this.spacer=null,this.dom=document.createElement("div"),this.dom.className="cm-gutter"+(this.config.class?" "+this.config.class:"");for(let r in n.domEventHandlers)this.dom.addEventListener(r,i=>{let s=i.target,o;if(s!=this.dom&&this.dom.contains(s)){for(;s.parentNode!=this.dom;)s=s.parentNode;let c=s.getBoundingClientRect();o=(c.top+c.bottom)/2}else o=i.clientY;let l=e.lineBlockAtHeight(o-e.documentTop);n.domEventHandlers[r](e,l,i)&&i.preventDefault()});this.markers=$S(n.markers(e)),n.initialSpacer&&(this.spacer=new YI(e,0,0,[n.initialSpacer(e)]),this.dom.appendChild(this.spacer.dom),this.spacer.dom.style.cssText+="visibility: hidden; pointer-events: none")}update(e){let n=this.markers;if(this.markers=$S(this.config.markers(e.view)),this.spacer&&this.config.updateSpacer){let i=this.config.updateSpacer(this.spacer.markers[0],e);i!=this.spacer.markers[0]&&this.spacer.update(e.view,0,0,[i])}let r=e.view.viewport;return!Ht.eq(this.markers,n,r.from,r.to)||(this.config.lineMarkerChange?this.config.lineMarkerChange(e):!1)}destroy(){for(let e of this.elements)e.destroy()}}class YI{constructor(e,n,r,i){this.height=-1,this.above=0,this.markers=[],this.dom=document.createElement("div"),this.dom.className="cm-gutterElement",this.update(e,n,r,i)}update(e,n,r,i){this.height!=n&&(this.height=n,this.dom.style.height=n+"px"),this.above!=r&&(this.dom.style.marginTop=(this.above=r)?r+"px":""),Fq(this.markers,i)||this.setMarkers(e,i)}setMarkers(e,n){let r="cm-gutterElement",i=this.dom.firstChild;for(let s=0,o=0;;){let l=o,c=ss(l,c,d)||o(l,c,d):o}return r}})}});class w0 extends ra{constructor(e){super(),this.number=e}eq(e){return this.number==e.number}toDOM(){return document.createTextNode(this.number)}}function T0(t,e){return t.state.facet(tl).formatNumber(e,t.state)}const Hq=Yf.compute([tl],t=>({class:"cm-lineNumbers",renderEmptyElements:!1,markers(e){return e.state.facet(Bq)},lineMarker(e,n,r){return r.some(i=>i.toDOM)?null:new w0(T0(e,e.state.doc.lineAt(n.from).number))},widgetMarker:(e,n,r)=>{for(let i of e.state.facet(Uq)){let s=i(e,n,r);if(s)return s}return null},lineMarkerChange:e=>e.startState.facet(tl)!=e.state.facet(tl),initialSpacer(e){return new w0(T0(e,VS(e.state.doc.lines)))},updateSpacer(e,n){let r=T0(n.view,VS(n.view.state.doc.lines));return r==e.number?e:new w0(r)},domEventHandlers:t.facet(tl).domEventHandlers,side:"before"}));function zq(t={}){return[tl.of(t),Dq(),Hq]}function VS(t){let e=9;for(;et.normalize("NFKD"):t=>t;class Cl{constructor(e,n,r=0,i=e.length,s,o){this.test=o,this.value={from:0,to:0},this.done=!1,this.matches=[],this.buffer="",this.bufferPos=0,this.iter=e.iterRange(r,i),this.bufferStart=r,this.normalize=s?l=>s(GS(l)):GS,this.query=this.normalize(n)}peek(){if(this.bufferPos==this.buffer.length){if(this.bufferStart+=this.buffer.length,this.iter.next(),this.iter.done)return-1;this.bufferPos=0,this.buffer=this.iter.value}return DR(this.buffer,this.bufferPos)}next(){for(;this.matches.length;)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let e=this.peek();if(e<0)return this.done=!0,this;let n=LK(e),r=this.bufferStart+this.bufferPos;this.bufferPos+=LR(e);let i=this.normalize(n);if(i.length)for(let s=0,o=r;;s++){let l=i.charCodeAt(s),c=this.match(l,o,this.bufferPos+this.bufferStart);if(s==i.length-1){if(c)return this.value=c,this;break}o==r&&sthis.to&&(this.curLine=this.curLine.slice(0,this.to-this.curLineStart)),this.iter.next())}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1,this.curLineStart>this.to?this.curLine="":this.getLine(0)}next(){for(let e=this.matchPos-this.curLineStart;;){this.re.lastIndex=e;let n=this.matchPos<=this.to&&this.re.exec(this.curLine);if(n){let r=this.curLineStart+n.index,i=r+n[0].length;if(this.matchPos=Nh(this.text,i+(r==i?1:0)),r==this.curLineStart+this.curLine.length&&this.nextLine(),(rthis.value.to)&&(!this.test||this.test(r,i,n)))return this.value={from:r,to:i,match:n},this;e=this.matchPos-this.curLineStart}else if(this.curLineStart+this.curLine.length=r||i.to<=n){let l=new fl(n,e.sliceString(n,r));return S0.set(e,l),l}if(i.from==n&&i.to==r)return i;let{text:s,from:o}=i;return o>n&&(s=e.sliceString(n,o)+s,o=n),i.to=this.to?this.to:this.text.lineAt(e).to}next(){for(;;){let e=this.re.lastIndex=this.matchPos-this.flat.from,n=this.re.exec(this.flat.text);if(n&&!n[0]&&n.index==e&&(this.re.lastIndex=e+1,n=this.re.exec(this.flat.text)),n){let r=this.flat.from+n.index,i=r+n[0].length;if((this.flat.to>=this.to||n.index+n[0].length<=this.flat.text.length-10)&&(!this.test||this.test(r,i,n)))return this.value={from:r,to:i,match:n},this.matchPos=Nh(this.text,i+(r==i?1:0)),this}if(this.flat.to==this.to)return this.done=!0,this;this.flat=fl.get(this.text,this.flat.from,this.chunkEnd(this.flat.from+this.flat.text.length*2))}}}typeof Symbol<"u"&&(XI.prototype[Symbol.iterator]=QI.prototype[Symbol.iterator]=function(){return this});function jq(t){try{return new RegExp(t,L1),!0}catch{return!1}}function Nh(t,e){if(e>=t.length)return e;let n=t.lineAt(e),r;for(;e=56320&&r<57344;)e++;return e}function Yb(t){let e=String(t.state.doc.lineAt(t.state.selection.main.head).number),n=Kn("input",{class:"cm-textfield",name:"line",value:e}),r=Kn("form",{class:"cm-gotoLine",onkeydown:s=>{s.keyCode==27?(s.preventDefault(),t.dispatch({effects:ic.of(!1)}),t.focus()):s.keyCode==13&&(s.preventDefault(),i())},onsubmit:s=>{s.preventDefault(),i()}},Kn("label",t.state.phrase("Go to line"),": ",n)," ",Kn("button",{class:"cm-button",type:"submit"},t.state.phrase("go")),Kn("button",{name:"close",onclick:()=>{t.dispatch({effects:ic.of(!1)}),t.focus()},"aria-label":t.state.phrase("close"),type:"button"},["×"]));function i(){let s=/^([+-])?(\d+)?(:\d+)?(%)?$/.exec(n.value);if(!s)return;let{state:o}=t,l=o.doc.lineAt(o.selection.main.head),[,c,d,f,p]=s,m=f?+f.slice(1):0,g=d?+d:l.number;if(d&&p){let S=g/100;c&&(S=S*(c=="-"?-1:1)+l.number/o.doc.lines),g=Math.round(o.doc.lines*S)}else d&&c&&(g=g*(c=="-"?-1:1)+l.number);let x=o.doc.line(Math.max(1,Math.min(o.doc.lines,g))),v=Pe.cursor(x.from+Math.max(0,Math.min(m,x.length)));t.dispatch({effects:[ic.of(!1),ft.scrollIntoView(v.from,{y:"center"})],selection:v}),t.focus()}return{dom:r}}const ic=bn.define(),KS=bo.define({create(){return!0},update(t,e){for(let n of e.effects)n.is(ic)&&(t=n.value);return t},provide:t=>kh.from(t,e=>e?Yb:null)}),$q=t=>{let e=Ah(t,Yb);if(!e){let n=[ic.of(!0)];t.state.field(KS,!1)==null&&n.push(bn.appendConfig.of([KS,Wq])),t.dispatch({effects:n}),e=Ah(t,Yb)}return e&&e.dom.querySelector("input").select(),!0},Wq=ft.baseTheme({".cm-panel.cm-gotoLine":{padding:"2px 6px 4px",position:"relative","& label":{fontSize:"80%"},"& [name=close]":{position:"absolute",top:"0",bottom:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:"0"}}}),Vq={highlightWordAroundCursor:!1,minSelectionLength:1,maxMatches:100,wholeWords:!1},Gq=tt.define({combine(t){return T1(t,Vq,{highlightWordAroundCursor:(e,n)=>e||n,minSelectionLength:Math.min,maxMatches:Math.min})}});function Kq(t){return[Zq,Qq]}const Yq=en.mark({class:"cm-selectionMatch"}),qq=en.mark({class:"cm-selectionMatch cm-selectionMatch-main"});function YS(t,e,n,r){return(n==0||t(e.sliceDoc(n-1,n))!=Cn.Word)&&(r==e.doc.length||t(e.sliceDoc(r,r+1))!=Cn.Word)}function Xq(t,e,n,r){return t(e.sliceDoc(n,n+1))==Cn.Word&&t(e.sliceDoc(r-1,r))==Cn.Word}const Qq=Ss.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.selectionSet||t.docChanged||t.viewportChanged)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=t.state.facet(Gq),{state:n}=t,r=n.selection;if(r.ranges.length>1)return en.none;let i=r.main,s,o=null;if(i.empty){if(!e.highlightWordAroundCursor)return en.none;let c=n.wordAt(i.head);if(!c)return en.none;o=n.charCategorizer(i.head),s=n.sliceDoc(c.from,c.to)}else{let c=i.to-i.from;if(c200)return en.none;if(e.wholeWords){if(s=n.sliceDoc(i.from,i.to),o=n.charCategorizer(i.head),!(YS(o,n,i.from,i.to)&&Xq(o,n,i.from,i.to)))return en.none}else if(s=n.sliceDoc(i.from,i.to),!s)return en.none}let l=[];for(let c of t.visibleRanges){let d=new Cl(n.doc,s,c.from,c.to);for(;!d.next().done;){let{from:f,to:p}=d.value;if((!o||YS(o,n,f,p))&&(i.empty&&f<=i.from&&p>=i.to?l.push(qq.range(f,p)):(f>=i.to||p<=i.from)&&l.push(Yq.range(f,p)),l.length>e.maxMatches))return en.none}}return en.set(l)}},{decorations:t=>t.decorations}),Zq=ft.baseTheme({".cm-selectionMatch":{backgroundColor:"#99ff7780"},".cm-searchMatch .cm-selectionMatch":{backgroundColor:"transparent"}}),Jq=({state:t,dispatch:e})=>{let{selection:n}=t,r=Pe.create(n.ranges.map(i=>t.wordAt(i.head)||Pe.cursor(i.head)),n.mainIndex);return r.eq(n)?!1:(e(t.update({selection:r})),!0)};function eX(t,e){let{main:n,ranges:r}=t.selection,i=t.wordAt(n.head),s=i&&i.from==n.from&&i.to==n.to;for(let o=!1,l=new Cl(t.doc,e,r[r.length-1].to);;)if(l.next(),l.done){if(o)return null;l=new Cl(t.doc,e,0,Math.max(0,r[r.length-1].from-1)),o=!0}else{if(o&&r.some(c=>c.from==l.value.from))continue;if(s){let c=t.wordAt(l.value.from);if(!c||c.from!=l.value.from||c.to!=l.value.to)continue}return l.value}}const tX=({state:t,dispatch:e})=>{let{ranges:n}=t.selection;if(n.some(s=>s.from===s.to))return Jq({state:t,dispatch:e});let r=t.sliceDoc(n[0].from,n[0].to);if(t.selection.ranges.some(s=>t.sliceDoc(s.from,s.to)!=r))return!1;let i=eX(t,r);return i?(e(t.update({selection:t.selection.addRange(Pe.range(i.from,i.to),!1),effects:ft.scrollIntoView(i.to)})),!0):!1},da=tt.define({combine(t){return T1(t,{top:!1,caseSensitive:!1,literal:!1,regexp:!1,wholeWord:!1,createPanel:e=>new pX(e),scrollToMatch:e=>ft.scrollIntoView(e)})}});function nX(t){return t?[da.of(t),Xb]:Xb}class ZI{constructor(e){this.search=e.search,this.caseSensitive=!!e.caseSensitive,this.literal=!!e.literal,this.regexp=!!e.regexp,this.replace=e.replace||"",this.valid=!!this.search&&(!this.regexp||jq(this.search)),this.unquoted=this.unquote(this.search),this.wholeWord=!!e.wholeWord}unquote(e){return this.literal?e:e.replace(/\\([nrt\\])/g,(n,r)=>r=="n"?` `:r=="r"?"\r":r=="t"?" ":"\\")}eq(e){return this.search==e.search&&this.replace==e.replace&&this.caseSensitive==e.caseSensitive&&this.regexp==e.regexp&&this.wholeWord==e.wholeWord}create(){return this.regexp?new oX(this):new iX(this)}getCursor(e,n=0,r){let i=e.doc?e:Wt.create({doc:e});return r==null&&(r=i.doc.length),this.regexp?Xa(this,i,n,r):qa(this,i,n,r)}}class JI{constructor(e){this.spec=e}}function qa(t,e,n,r){return new Cl(e.doc,t.unquoted,n,r,t.caseSensitive?void 0:i=>i.toLowerCase(),t.wholeWord?rX(e.doc,e.charCategorizer(e.selection.main.head)):void 0)}function rX(t,e){return(n,r,i,s)=>((s>n||s+i.length=n)return null;i.push(r.value)}return i}highlight(e,n,r,i){let s=qa(this.spec,e,Math.max(0,n-this.spec.unquoted.length),Math.min(r+this.spec.unquoted.length,e.doc.length));for(;!s.next().done;)i(s.value.from,s.value.to)}}function Xa(t,e,n,r){return new XI(e.doc,t.search,{ignoreCase:!t.caseSensitive,test:t.wholeWord?sX(e.charCategorizer(e.selection.main.head)):void 0},n,r)}function Rh(t,e){return t.slice(xi(t,e,!1),e)}function Ih(t,e){return t.slice(e,xi(t,e))}function sX(t){return(e,n,r)=>!r[0].length||(t(Rh(r.input,r.index))!=Cn.Word||t(Ih(r.input,r.index))!=Cn.Word)&&(t(Ih(r.input,r.index+r[0].length))!=Cn.Word||t(Rh(r.input,r.index+r[0].length))!=Cn.Word)}class oX extends JI{nextMatch(e,n,r){let i=Xa(this.spec,e,r,e.doc.length).next();return i.done&&(i=Xa(this.spec,e,0,n).next()),i.done?null:i.value}prevMatchInRange(e,n,r){for(let i=1;;i++){let s=Math.max(n,r-i*1e4),o=Xa(this.spec,e,s,r),l=null;for(;!o.next().done;)l=o.value;if(l&&(s==n||l.from>s+10))return l;if(s==n)return null}}prevMatch(e,n,r){return this.prevMatchInRange(e,0,n)||this.prevMatchInRange(e,r,e.doc.length)}getReplacement(e){return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g,(n,r)=>{if(r=="&")return e.match[0];if(r=="$")return"$";for(let i=r.length;i>0;i--){let s=+r.slice(0,i);if(s>0&&s=n)return null;i.push(r.value)}return i}highlight(e,n,r,i){let s=Xa(this.spec,e,Math.max(0,n-250),Math.min(r+250,e.doc.length));for(;!s.next().done;)i(s.value.from,s.value.to)}}const Cc=bn.define(),P1=bn.define(),ao=bo.define({create(t){return new _0(qb(t).create(),null)},update(t,e){for(let n of e.effects)n.is(Cc)?t=new _0(n.value.create(),t.panel):n.is(P1)&&(t=new _0(t.query,n.value?F1:null));return t},provide:t=>kh.from(t,e=>e.panel)});class _0{constructor(e,n){this.query=e,this.panel=n}}const aX=en.mark({class:"cm-searchMatch"}),lX=en.mark({class:"cm-searchMatch cm-searchMatch-selected"}),uX=Ss.fromClass(class{constructor(t){this.view=t,this.decorations=this.highlight(t.state.field(ao))}update(t){let e=t.state.field(ao);(e!=t.startState.field(ao)||t.docChanged||t.selectionSet||t.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:t,panel:e}){if(!e||!t.spec.valid)return en.none;let{view:n}=this,r=new xc;for(let i=0,s=n.visibleRanges,o=s.length;is[i+1].from-500;)c=s[++i].to;t.highlight(n.state,l,c,(d,f)=>{let p=n.state.selection.ranges.some(m=>m.from==d&&m.to==f);r.add(d,f,p?lX:aX)})}return r.finish()}},{decorations:t=>t.decorations});function $c(t){return e=>{let n=e.state.field(ao,!1);return n&&n.query.spec.valid?t(e,n):B1(e)}}const Oh=$c((t,{query:e})=>{let{to:n}=t.state.selection.main,r=e.nextMatch(t.state,n,n);if(!r)return!1;let i=Pe.single(r.from,r.to),s=t.state.facet(da);return t.dispatch({selection:i,effects:[U1(t,r),s.scrollToMatch(i.main,t)],userEvent:"select.search"}),tO(t),!0}),Mh=$c((t,{query:e})=>{let{state:n}=t,{from:r}=n.selection.main,i=e.prevMatch(n,r,r);if(!i)return!1;let s=Pe.single(i.from,i.to),o=t.state.facet(da);return t.dispatch({selection:s,effects:[U1(t,i),o.scrollToMatch(s.main,t)],userEvent:"select.search"}),tO(t),!0}),cX=$c((t,{query:e})=>{let n=e.matchAll(t.state,1e3);return!n||!n.length?!1:(t.dispatch({selection:Pe.create(n.map(r=>Pe.range(r.from,r.to))),userEvent:"select.search.matches"}),!0)}),dX=({state:t,dispatch:e})=>{let n=t.selection;if(n.ranges.length>1||n.main.empty)return!1;let{from:r,to:i}=n.main,s=[],o=0;for(let l=new Cl(t.doc,t.sliceDoc(r,i));!l.next().done;){if(s.length>1e3)return!1;l.value.from==r&&(o=s.length),s.push(Pe.range(l.value.from,l.value.to))}return e(t.update({selection:Pe.create(s,o),userEvent:"select.search.matches"})),!0},qS=$c((t,{query:e})=>{let{state:n}=t,{from:r,to:i}=n.selection.main;if(n.readOnly)return!1;let s=e.nextMatch(n,r,r);if(!s)return!1;let o=s,l=[],c,d,f=[];o.from==r&&o.to==i&&(d=n.toText(e.getReplacement(o)),l.push({from:o.from,to:o.to,insert:d}),o=e.nextMatch(n,o.from,o.to),f.push(ft.announce.of(n.phrase("replaced match on line $",n.doc.lineAt(r).number)+".")));let p=t.state.changes(l);return o&&(c=Pe.single(o.from,o.to).map(p),f.push(U1(t,o)),f.push(n.facet(da).scrollToMatch(c.main,t))),t.dispatch({changes:p,selection:c,effects:f,userEvent:"input.replace"}),!0}),fX=$c((t,{query:e})=>{if(t.state.readOnly)return!1;let n=e.matchAll(t.state,1e9).map(i=>{let{from:s,to:o}=i;return{from:s,to:o,insert:e.getReplacement(i)}});if(!n.length)return!1;let r=t.state.phrase("replaced $ matches",n.length)+".";return t.dispatch({changes:n,effects:ft.announce.of(r),userEvent:"input.replace.all"}),!0});function F1(t){return t.state.facet(da).createPanel(t)}function qb(t,e){var n,r,i,s,o;let l=t.selection.main,c=l.empty||l.to>l.from+100?"":t.sliceDoc(l.from,l.to);if(e&&!c)return e;let d=t.facet(da);return new ZI({search:((n=e?.literal)!==null&&n!==void 0?n:d.literal)?c:c.replace(/\n/g,"\\n"),caseSensitive:(r=e?.caseSensitive)!==null&&r!==void 0?r:d.caseSensitive,literal:(i=e?.literal)!==null&&i!==void 0?i:d.literal,regexp:(s=e?.regexp)!==null&&s!==void 0?s:d.regexp,wholeWord:(o=e?.wholeWord)!==null&&o!==void 0?o:d.wholeWord})}function eO(t){let e=Ah(t,F1);return e&&e.dom.querySelector("[main-field]")}function tO(t){let e=eO(t);e&&e==t.root.activeElement&&e.select()}const B1=t=>{let e=t.state.field(ao,!1);if(e&&e.panel){let n=eO(t);if(n&&n!=t.root.activeElement){let r=qb(t.state,e.query.spec);r.valid&&t.dispatch({effects:Cc.of(r)}),n.focus(),n.select()}}else t.dispatch({effects:[P1.of(!0),e?Cc.of(qb(t.state,e.query.spec)):bn.appendConfig.of(Xb)]});return!0},nO=t=>{let e=t.state.field(ao,!1);if(!e||!e.panel)return!1;let n=Ah(t,F1);return n&&n.dom.contains(t.root.activeElement)&&t.focus(),t.dispatch({effects:P1.of(!1)}),!0},hX=[{key:"Mod-f",run:B1,scope:"editor search-panel"},{key:"F3",run:Oh,shift:Mh,scope:"editor search-panel",preventDefault:!0},{key:"Mod-g",run:Oh,shift:Mh,scope:"editor search-panel",preventDefault:!0},{key:"Escape",run:nO,scope:"editor search-panel"},{key:"Mod-Shift-l",run:dX},{key:"Mod-Alt-g",run:$q},{key:"Mod-d",run:tX,preventDefault:!0}];class pX{constructor(e){this.view=e;let n=this.query=e.state.field(ao).query.spec;this.commit=this.commit.bind(this),this.searchField=Kn("input",{value:n.search,placeholder:Br(e,"Find"),"aria-label":Br(e,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=Kn("input",{value:n.replace,placeholder:Br(e,"Replace"),"aria-label":Br(e,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=Kn("input",{type:"checkbox",name:"case",form:"",checked:n.caseSensitive,onchange:this.commit}),this.reField=Kn("input",{type:"checkbox",name:"re",form:"",checked:n.regexp,onchange:this.commit}),this.wordField=Kn("input",{type:"checkbox",name:"word",form:"",checked:n.wholeWord,onchange:this.commit});function r(i,s,o){return Kn("button",{class:"cm-button",name:i,onclick:s,type:"button"},o)}this.dom=Kn("div",{onkeydown:i=>this.keydown(i),class:"cm-search"},[this.searchField,r("next",()=>Oh(e),[Br(e,"next")]),r("prev",()=>Mh(e),[Br(e,"previous")]),r("select",()=>cX(e),[Br(e,"all")]),Kn("label",null,[this.caseField,Br(e,"match case")]),Kn("label",null,[this.reField,Br(e,"regexp")]),Kn("label",null,[this.wordField,Br(e,"by word")]),...e.state.readOnly?[]:[Kn("br"),this.replaceField,r("replace",()=>qS(e),[Br(e,"replace")]),r("replaceAll",()=>fX(e),[Br(e,"replace all")])],Kn("button",{name:"close",onclick:()=>nO(e),"aria-label":Br(e,"close"),type:"button"},["×"])])}commit(){let e=new ZI({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});e.eq(this.query)||(this.query=e,this.view.dispatch({effects:Cc.of(e)}))}keydown(e){Rq(this.view,e,"search-panel")?e.preventDefault():e.keyCode==13&&e.target==this.searchField?(e.preventDefault(),(e.shiftKey?Mh:Oh)(this.view)):e.keyCode==13&&e.target==this.replaceField&&(e.preventDefault(),qS(this.view))}update(e){for(let n of e.transactions)for(let r of n.effects)r.is(Cc)&&!r.value.eq(this.query)&&this.setQuery(r.value)}setQuery(e){this.query=e,this.searchField.value=e.search,this.replaceField.value=e.replace,this.caseField.checked=e.caseSensitive,this.reField.checked=e.regexp,this.wordField.checked=e.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(da).top}}function Br(t,e){return t.state.phrase(e)}const Mf=30,Df=/[\s\.,:;?!]/;function U1(t,{from:e,to:n}){let r=t.state.doc.lineAt(e),i=t.state.doc.lineAt(n).to,s=Math.max(r.from,e-Mf),o=Math.min(i,n+Mf),l=t.state.sliceDoc(s,o);if(s!=r.from){for(let c=0;cl.length-Mf;c--)if(!Df.test(l[c-1])&&Df.test(l[c])){l=l.slice(0,c);break}}return ft.announce.of(`${t.state.phrase("current match")}. ${l} ${t.state.phrase("on line")} ${r.number}.`)}const mX=ft.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),Xb=[ao,w1.low(uX),mX],gX=({text:t})=>{const e=T.useRef(null),n=T.useRef(null);return T.useEffect(()=>{if(e.current){const r=[zq(),ft.editable.of(!1),nX({top:!0}),Kq(),WI.of(hX)],i=Wt.create({doc:t,extensions:r}),s=new ft({state:i,parent:e.current});return n.current=s,()=>s.destroy()}},[t]),T.useEffect(()=>{n.current&&setTimeout(()=>B1(n.current),0)},[n.current]),w.jsx("div",{onKeyDownCapture:r=>r.key==="Escape"&&r.stopPropagation(),className:"[&_.cm-search_*]:hidden [&_.cm-gutters]:bg-white [&_.cm-gutters]:text-[#bbb] [&_.cm-gutters]:border-0 [&_.cm-gutters]:ps-[0.5em] [&_.cm-gutters]:pe-[1.2em] [&_.cm-search]:bg-white [&_.cm-scroller>div:nth-child(2)]:flex-1 [&_.cm-scroller>div:nth-child(2)]:[white-space:break-spaces] [&_.cm-panels]:border-none [&_.cm-search_input:first-child]:block [&_.cm-search_input:first-child]:rounded-[5px] [&_.cm-search_input:first-child]:w-[300px] [&_.cm-panels]:sticky [&_.cm-panels]:translate-y-0 [&_.cm-panels]:h-[39px] [&_.cm-panels]:bg-white [&_.cm-panels]:-translate-y-[100%] [&_.cm-panels]:pl-[20px]",children:w.jsx("div",{ref:e,className:"your-class-name"})})},bX=({log:t})=>{const{openDialog:e,DialogComponent:n,closeDialog:r}=zA(),i=T.useRef(null),s=o=>{e("",w.jsxs("pre",{ref:i,className:"group rounded-[12px] fixed-scroll font-light font-ibm-plex-mono border-y-[10px] border-white text-wrap text-[#333] relative overflow-auto h-[100%]",children:[w.jsx("div",{className:"invisble w-fit [justify-self:end] z-[999] group-hover:visible flex fixed top-[10px] right-[10px] justify-end",children:w.jsxs("div",{className:"flex justify-end bg-white p-[10px] gap-[20px] rounded-lg w-fit",children:[w.jsx(Xn,{value:"Copy",side:"top",children:w.jsx("img",{src:"icons/copy.svg",alt:"",onClick:()=>hl(o,i?.current||void 0),className:"cursor-pointer"})}),w.jsx(Xn,{value:"Close",side:"top",children:w.jsx(gl,{onClick:()=>r(),size:18,className:"cursor-pointer"})})]})}),w.jsx(gX,{text:o})]}),{height:"90vh",width:"min(90vw, 1200px)"})};return w.jsxs("div",{className:On("flex max-h-[200px] w-full overflow-hidden group relative font-ubuntu-mono gap-[5px] px-[20px] text-[14px] transition-all hover:bg-[#FAFAFA]"),children:[w.jsxs("div",{className:"absolute hidden z-10 group-hover:flex right-[10px] top-[10px] gap-[5px]",children:[w.jsx(Xn,{value:"Copy",side:"top",children:w.jsx("div",{onClick:()=>hl(t?.message||""),className:"cursor-pointer size-[28px] flex justify-center items-center bg-white hover:bg-[#F3F5F9] border border-[#EEEEEE] hover:border-[#E9EBEF] rounded-[6px]",children:w.jsx("img",{src:"icons/copy.svg",alt:""})})}),w.jsx(Xn,{value:"Expand",side:"top",children:w.jsx("div",{onClick:()=>s(t?.message||""),className:"cursor-pointer size-[28px] flex justify-center items-center bg-white hover:bg-[#F3F5F9] border border-[#EEEEEE] hover:border-[#E9EBEF] rounded-[6px]",children:w.jsx("img",{src:"icons/expand.svg",alt:""})})})]}),w.jsx("pre",{className:Al("max-w-[-webkit-fill-available] border-y-[10px] border-white group-hover:border-[#FAFAFA] font-light font-ibm-plex-mono pe-[10px] text-wrap"),children:t?.message?.trim()}),w.jsx(n,{})]})},EX=({messagesRef:t,filteredLogs:e})=>w.jsx("div",{className:"p-[6px] overflow-hidden h-[calc(100%-12px)] rounded-[6px]",children:w.jsxs("div",{className:"pt-0 flex-1 border bg-white h-full rounded-[3px]",children:[w.jsxs("div",{className:"flex items-center min-h-[48px] text-[14px] font-medium border-b border-[#EDEFF3]",children:[w.jsx("div",{className:"w-[86px] border-e border-[#EDEFF3] min-h-[48px] flex items-center ps-[10px]",children:"Level"}),w.jsx("div",{className:"flex-1 ps-[10px]",children:"Message"})]}),w.jsx("div",{ref:t,className:"rounded-[8px] h-[calc(100%-60px)] overflow-auto bg-white fixed-scroll text-[14px] font-normal",children:e.map((n,r)=>w.jsxs("div",{className:"flex group hover:bg-[#FAFAFA] min-h-[48px] border-t border-[#EDEFF3] font-ibm-plex-mono [&:last-child]:border-b [&:first-child]:border-[0px] items-stretch",children:[w.jsx("div",{className:"min-w-[86px] w-[86px] border-e border-[#EDEFF3] min-h-[48px] flex ps-[10px] pt-[10px] capitalize",children:n.level?.toLowerCase()}),w.jsx(bX,{log:n})]},r))})]})}),yX=({event:t})=>w.jsx("div",{className:"h-full group p-[20px] bg-[#ebecf0] text-[13px] text-[#ef5350] z-10",children:w.jsxs("pre",{className:Al("p-[10px] max-w-[-webkit-fill-available] pe-[10px] text-wrap break-words bg-white rounded-[8px] h-full overflow-auto group relative"),children:[w.jsx("div",{className:"sticky h-0 hidden z-10 group-hover:block [direction:rtl] top-[10px] right-[10px] gap-[10px]",children:w.jsx(Xn,{value:"Copy",side:"top",children:w.jsx("img",{src:"icons/copy.svg",sizes:"18",alt:"",onClick:()=>hl(t?.error||""),className:"cursor-pointer"})})}),t?.error]})}),xX=t=>t.find(e=>e.selected)?.id||t[0]?.id||null,vX=({event:t,closeLogs:e,regenerateMessageFn:n,resendMessageFn:r,flaggedChanged:i,sameTraceMessages:s})=>{const[o,l]=T.useState(null),[c,d]=cG("filters",[]),[f,p]=T.useState(xX(c)),[m,g]=T.useState(null),[x,v]=T.useState([]),S=T.useRef(null),C=T.useRef(null);T.useEffect(()=>{d(I=>I.map(G=>(G.selected=G.id===f,G)))},[f]),T.useEffect(()=>{t?.id&&C.current?.resize(50)},[t?.id]),T.useEffect(()=>{(async()=>{const D=Object.keys(o||{}).length;if(m&&o)if(!D&&o)v(m);else{const G=await sG(t?.trace_id,o);v(G),d(X=>{if(!X.length&&D){const Y={id:Date.now(),def:o,name:"Logs"};return p(Y.id),[Y]}const P=X.find(Y=>Y.id===f);return P?(P.def=o,[...X]):X})}!o&&m?.length&&l({})})()},[m,o]),T.useEffect(()=>{!t&&m&&(g(null),v([]))},[t]),T.useEffect(()=>{if(!t?.trace_id)return;(async()=>{const D=await cb(t.trace_id);g(D)})()},[t?.trace_id]),T.useEffect(()=>{if(!t?.trace_id)return;const I=D=>{D.detail.trace_id===t.trace_id&&cb(t.trace_id).then(g)};return window.addEventListener("new-log",I),()=>window.removeEventListener("new-log",I)},[t?.trace_id]);const A=I=>{const D=c.findIndex(X=>X.id===I);if(D===-1)return;const G=c.filter(X=>X.id!==I);if(d(G),f===I){const X=G?.[(D||1)-1]?.id||G?.[0]?.id||null;p(X)}G.length||l({})},k=t&&!!m?.length&&!!c?.length,M=!1;Object.entries(t?.data?.canned_responses||{}).map(([I,D])=>({id:I,value:D}));const F=t?.serverStatus==="error";return w.jsxs("div",{className:On("w-full h-full animate-fade-in duration-200 overflow-auto flex flex-col justify-start pt-0 pe-0 bg-[#FBFBFB]"),children:[w.jsx(ZG,{event:t||null,closeLogs:e,sameTraceMessages:s,resendMessageFn:r,regenerateMessageFn:n,className:On("shadow-main h-[60px] min-h-[60px]",Object.keys(o||{}).length?"border-[#F3F5F9]":""),flaggedChanged:i}),w.jsx("div",{className:"ps-[20px] pt-[10px] flex items-center gap-[3px] text-[14px] font-normal bg-white",children:w.jsx(BA,{textToCopy:t?.trace_id?.split("::")?.[0],preText:"Trace ID: ",text:`${t?.trace_id?.split("::")?.[0]}`,className:"whitespace-nowrap [&_span]:text-ellipsis [&_span]:overflow-hidden [&_span]:block"})}),w.jsxs(CK,{direction:"vertical",className:On("w-full h-full overflow-auto flex flex-col justify-start pt-0 pe-0 bg-[#FBFBFB]"),children:[w.jsx(qT,{ref:C,minSize:0,maxSize:F?99:0,defaultSize:F?50:0,children:F&&w.jsx(yX,{event:t})}),w.jsx(AK,{withHandle:!0,className:On(!F&&"hidden")}),w.jsxs(qT,{minSize:F?0:100,maxSize:F?99:100,defaultSize:F?50:100,className:"flex flex-col bg-white",children:[M,w.jsx("div",{className:Xe("flex justify-between bg-white z-[1] items-center min-h-[58px] h-[58px] p-[10px] pb-[4px] pe-0",k&&"min-h-0 h-0"),children:!k&&w.jsx(HT,{showDropdown:!0,filterId:f||void 0,def:structuredClone(c.find(I=>f===I.id)?.def||null),applyFn:(I,D,G)=>{setTimeout(()=>l({types:I,level:D,content:G}),0)}})}),k&&w.jsx(QG,{currFilterTabs:f,filterTabs:c,setFilterTabs:d,setCurrFilterTabs:p}),t&&!!m?.length&&k&&w.jsx(HT,{showTags:!0,showDropdown:!0,deleteFilterTab:A,className:Xe(!x?.length&&"",!m?.length&&"absolute"),filterId:f||void 0,def:structuredClone(c.find(I=>f===I.id)?.def||null),applyFn:(I,D,G)=>{setTimeout(()=>l({types:I,level:D,content:G}),0)}}),!t&&w.jsx(o0,{title:"Feeling curious?",subTitle:"Select a message for additional actions and information about its process."}),t&&m&&!m?.length&&w.jsx(o0,{imgClassName:"w-[68px] h-[48px]",imgUrl:"logo-muted.svg",title:"Whoopsie!",subTitle:"The logs for this message weren't found in cache. Try regenerating it to get fresh logs.",className:On(F&&"translate-y-[0px]")}),t&&!!m?.length&&!x.length&&w.jsx(o0,{title:"No logs for the current filters",className:On(F&&"translate-y-[0px]")}),t&&!!x.length&&w.jsx("div",{className:"ps-[10px] overflow-auto h-[-webkit-fill-available]",children:w.jsx(EX,{messagesRef:S,filteredLogs:x})})]})]})]})},wX=T.memo(vX,(t,e)=>t.event===e.event&&t.sameTraceMessages===e.sameTraceMessages),TX=({date:t,isFirst:e,bgColor:n})=>w.jsx("div",{className:"flex justify-center min-h-[30px] z-[1] bg-white h-[30px] pb-[4px] mb-[14px] pt-[4px] sticky -top-[1px]",children:w.jsxs("div",{className:Xe("text-center flex justify-center max-w-[min(1000px,100%)] min-w-[min(1000px,100%)]",e&&"pt-[1px] !mt-0",n),children:[w.jsx("div",{className:"[box-shadow:0_-0.6px_0px_0px_#F3F5F9] h-full -translate-y-[-50%] flex-1 "}),w.jsx("div",{className:"w-[130px] border-[0.6px] border-muted font-light text-[12px] bg-white text-[#656565] flex items-center justify-center rounded-[6px]",children:sA(t)}),w.jsx("div",{className:"[box-shadow:0_-0.6px_0px_0px_#F3F5F9] h-full -translate-y-[-50%] flex-1"})]})}),SX=T.memo(TX);function XS(t){const e=window.AudioContext||window.webkitAudioContext,n=new e,r=(s,o)=>{const l=n.createOscillator(),c=n.createGain();l.type="sine",l.frequency.setValueAtTime(o,s),c.gain.setValueAtTime(.5,s),c.gain.exponentialRampToValueAtTime(.001,s+.15),l.connect(c),c.connect(n.destination),l.start(s),l.stop(s+.15)},i=n.currentTime;if(t){r(i,660),r(i+.2,880);return}r(i,880),r(i+.2,660)}const _X=()=>{const t=T.useRef(null),e=T.useRef(null),n=T.useRef(null),r=T.useRef(null),[i,s]=T.useState(""),[o,l]=T.useState(0),[c,d]=T.useState([]),[f,p]=T.useState(!1),[m,g]=T.useState(!1),[x,v]=T.useState(""),[S,C]=T.useState(!0),{openQuestionDialog:A,closeQuestionDialog:k}=tG(),[M,F]=T.useState(!1),[I,D]=T.useState(null),[G,X]=T.useState(null),[P,Y]=T.useState(!1),[z,ie]=T.useState({}),[Z,ee]=T.useState(!1),[ae,de]=lt(SF),[j]=lt(tp),[W,O]=lt(As),[U]=lt(oa),[Q,R]=lt(zE),[,oe]=lt(TF),[,pe]=lt(rp),[ue,J]=T.useState([]),he=T.useRef(null),[_e,ke]=T.useState(0),Ve=T.useCallback(()=>{ke(Re=>Re+1)},[]),ot=T.useCallback(()=>{he.current&&(he.current.close(),he.current=null)},[]),qe=T.useRef(0),kt=T.useRef(null);T.useEffect(()=>{if(!W?.id||W?.id===Hi)return;he.current&&he.current.close(),kt.current!==W.id&&(J([]),qe.current=0,kt.current=W.id);const Me=`${lo}/sessions/${W.id}/events?sse=true&min_offset=${qe.current}&wait_for_data=60`,Ge=new EventSource(Me);return he.current=Ge,Ge.onmessage=Ke=>{try{const bt=JSON.parse(Ke.data);bt.offset!==void 0&&(qe.current=Math.max(qe.current,bt.offset+1)),J(vt=>[...vt,bt])}catch(bt){console.error("Error parsing SSE event:",bt)}},Ge.onerror=()=>{Ge.close(),he.current=null,setTimeout(()=>{ke(Ke=>Ke+1)},1e3)},()=>{Ge.close(),he.current=null}},[W?.id,_e]);const fn=()=>{s(""),l(0),d([]),p(!1),D(null)},nt=Re=>(Me,Ge)=>{const Ke=Re===c.length-1,bt=c[Re].offset;if(Ke)return D(null),Ct(Re,Me,bt,Ge);A("Are you sure?","Resending this message would cause all of the following messages in the session to disappear.",[{text:"Resend Anyway",onClick:()=>{D(null),k(),Ct(Re,Me,bt,Ge)},isMainAction:!0}])},Yt=Re=>Me=>{const Ge=Re===c.length-1,Ke=c.slice(0,Re+1),bt=Ke.findLastIndex($t=>$t.source==="customer"&&$t.kind==="message"),jt=Ke[bt]?.offset??c.length-1;if(Ge)return D(null),Pn(bt,Me,jt);A("Are you sure?","Regenerating this message would cause all of the following messages in the session to disappear.",[{text:"Regenerate Anyway",onClick:()=>{D(null),k(),Pn(bt,Me,jt)},isMainAction:!0}])},Ct=async(Re,Me,Ge,Ke)=>{const bt=c[Re],vt=await JS(`sessions/${Me}/events?min_offset=${Ge}`).catch(jt=>({error:jt}));if(vt?.error){Ir.error(vt.error.message||vt.error);return}ot?.(),l(Ge),d(jt=>jt.slice(0,Re)),ye(Ke??bt.data?.message)},Pn=async(Re,Me,Ge)=>{Ct(Re,Me,Ge)},Fn=()=>{if(W?.id===Hi)return;const Re=ue?.at(-1),Me=ue?.findLast(Dt=>Dt.kind==="status");if(!Re)return;const Ge=Re?.offset;(Ge||Ge===0)&&l(Ge+1);const Ke=mB(ue||[],Dt=>Dt?.trace_id.split("::")[0]),bt=ue?.filter(Dt=>Dt.kind==="message")||[],vt=bt.map((Dt,$t)=>{const qt={...Dt},V=Ke?.[Dt.trace_id.split("::")[0]]?.at(-1)?.data;return qt.serverStatus=V?.status||(bt[$t+1]?"ready":null),qt.serverStatus==="error"&&(qt.error=V?.data?.exception),qt});d(Dt=>{const $t=Dt.findLast(V=>V.source==="customer");if($t?.source==="customer"&&Ke?.[$t?.trace_id]&&($t.serverStatus=Ke[$t.trace_id].at(-1)?.data?.status||$t.serverStatus,$t.serverStatus==="error"&&($t.error=Ke[$t.trace_id].at(-1)?.data?.data?.exception)),!vt?.length)return[...Dt];ae?.data?.message&&de(NA());const qt=[];for(const V of[Dt,vt])for(const te of V)qt[te.offset]=te;return qt.filter(V=>V)});const jt=Me?.data?.status,fr=bt.some(Dt=>Dt?.data?.chunks!==void 0);bt?.length&&(m||f)&&XS(!0),jt?(g(jt==="processing"),jt==="processing"&&v(Me?.data?.data?.stage??"Thinking"),p(jt==="typing"&&!fr)):fr&&p(!1),J([])},on=()=>{t?.current?.scrollIntoView?.({behavior:S?"instant":"smooth"}),t?.current&&S&&C(!1)},dr=()=>{C(!0),Q&&W?.id!==Hi&&R(null),fn(),n?.current?.focus()},Mn=async()=>{const Me=(await a_("Parlant-flags","message_flags","sessionIndex",W?.id,{name:"sessionIndex",keyPath:"sessionId"})).reduce((Ge,Ke)=>(Ge[Ke.traceId]=Ke.flagValue,Ge),{});ie(Me)};T.useEffect(()=>{Mn()},[W?.id,Z]),T.useEffect(()=>{ooe(I),[I]),T.useEffect(Fn,[ue]),T.useEffect(on,[c?.length,ae,S]),T.useEffect(dr,[W?.id]),T.useEffect(()=>{(m||f)&&t?.current?.scrollIntoView({behavior:"smooth"})},[m,f]),T.useEffect(()=>{j&&U?.id&&X(!j?.find(Re=>Re.id===U?.id))},[j,U?.id]);const Qn=Re=>{const Me=Re?.data?.chunks;return Me===void 0?!1:Me.length===0?!0:Me[Me.length-1]!==null},li=T.useRef(new Map);T.useEffect(()=>{if(!W?.id||W?.id===Hi)return;const Re=c.filter(Qn),Me=li.current;for(const[Ge,Ke]of Me)Re.some(vt=>vt.id===Ge)||(Ke.close(),Me.delete(Ge));for(const Ge of Re){if(!Ge.id||Me.has(Ge.id))continue;const Ke=new EventSource(`${lo}/sessions/${W.id}/events/${Ge.id}?sse=true`);Ke.onmessage=bt=>{try{const vt=JSON.parse(bt.data);d(fr=>fr.map(Dt=>Dt.id===Ge.id?{...Dt,data:{...Dt.data,...vt.data}}:Dt));const jt=vt?.data?.chunks;jt&&jt.length>0&&jt[jt.length-1]===null&&(Ke.close(),Me.delete(Ge.id))}catch(vt){console.error("Error parsing SSE event:",vt)}},Ke.onerror=bt=>{console.error("SSE connection error:",bt),Ke.close(),Me.delete(Ge.id)},Me.set(Ge.id,Ke)}return()=>{for(const Ge of Me.values())Ge.close();Me.clear()}},[c,W?.id]);const ce=async()=>{if(!Q)return;const{customer_id:Re,title:Me}=Q;return dv("sessions?allow_greeting=false",{customer_id:Re,agent_id:U?.id,title:Me}).then(Ge=>(Q&&(O(Ge),R(null)),pe(Ke=>[...Ke,Ge]),Ge)).catch(()=>{Ir.error("Something went wrong")})},ye=async Re=>{de(Ke=>({...Ke,sessionId:W?.id,data:{message:Re}})),s("");const Me=Q?(await ce())?.id:W?.id;dv(`sessions/${Me}/events?moderation=${M?"auto":"none"}`,{kind:"message",message:Re,source:"customer"}).then(()=>{XS(),Ve()}).catch(()=>Ir.error("Something went wrong"))},Qe=Re=>{Re.key==="Enter"&&!Re.shiftKey?(Re.preventDefault(),e?.current?.click()):Re.key==="Enter"&&Re.shiftKey&&Re.preventDefault()},ut=W?.id===Hi&&!ae?.id||W?.id!==Hi&&ae?.sessionId===W?.id,rt=(!c?.length||ut)&&ae?.data?.message?[...c,ae]:c,an=rt.some(Re=>{const Me=Re?.data?.chunks;return Me!==void 0&&(Me.length===0||Me[Me.length-1]!==null)}),Zn=Re=>Me=>{Me.index=Re,D(Me.id===I?.id?null:Me)};return w.jsx(w.Fragment,{children:w.jsxs("div",{ref:r,className:Xe("flex items-center h-full w-full bg-white gap-[14px] rounded-[10px]",I&&"bg-green-light"),children:[w.jsx("div",{className:Xe("h-full w-full pb-[14px] pt-[10px] rounded-[10px] flex flex-col transition-all duration-500 bg-white",I&&"w-[calc(100%-min(700px,35vw))]"),children:w.jsx("div",{className:"h-full flex flex-col rounded-[10px] m-auto w-full min-w-[unset]",children:w.jsxs("div",{className:Xe("flex flex-col rounded-es-[16px] rounded-ee-[16px] items-center bg-white mx-auto w-full flex-1 overflow-hidden"),children:[w.jsxs("div",{className:On("messages fixed-scroll flex-1 flex flex-col w-full pb-4 overflow-x-hidden"),"aria-live":"polite",role:"log","aria-label":"Chat messages",children:[rt.map((Re,Me)=>w.jsxs(we.Fragment,{children:[!Ev(c[Me-1]?.creation_utc,Re.creation_utc)&&w.jsx(SX,{date:Re.creation_utc,isFirst:!Me,bgColor:"bg-white"}),w.jsx("div",{ref:t,className:"flex snap-end flex-col max-w-[min(1020px,100%)] w-[1020px] self-center",children:w.jsx(eG,{flaggedChanged:()=>{ee(Ge=>!Ge)},flagged:z[Re.trace_id],isFirstMessageInDate:!Ev(c[Me-1]?.creation_utc,Re.creation_utc),isRegenerateHidden:!!G,event:Re,sameTraceMessages:rt.filter(Ge=>Ge.trace_id===Re.trace_id),isContinual:Re.trace_id===rt[Me-1]?.trace_id&&Re.source===rt[Me-1]?.source||Re.source==="customer"&&rt[Me-1]?.source==="customer",regenerateMessageFn:Yt(Me),resendMessageFn:nt(Me),showLogsForMessage:I,showLogs:Zn(Me)})})]},(Re.trace_id||0)+`${Me}`)),(f&&!an||m)&&w.jsxs("div",{ref:t,className:"flex snap-end max-w-[min(1020px,100%)] w-[1020px] self-center",children:[w.jsx("div",{className:"bubblesWrapper snap-end","aria-hidden":"true",children:w.jsx("div",{className:"bubbles"})}),f&&!an&&w.jsx("p",{className:Xe("flex items-center font-normal text-[#A9AFB7] text-[14px] font-inter"),children:"Typing..."}),m&&w.jsxs("p",{className:Xe("flex items-center font-normal text-[#A9AFB7] text-[14px] font-inter"),children:[x,"..."]})]})]}),w.jsxs("div",{className:Xe("w-full flex justify-between",G&&"hidden"),children:[w.jsx(Qa,{}),w.jsxs("div",{className:"group relative border flex-1 border-muted border-solid rounded-[10px] flex flex-row justify-center items-center bg-white p-[0.9rem] ps-[14px] pe-0 h-[48.67px] max-w-[1000px] mb-[26px]",children:[w.jsxs(rA,{open:P,onOpenChange:Y,children:[w.jsx(iA,{className:"outline-none","data-testid":"menu-button",tabIndex:-1,onClick:Re=>Re.stopPropagation(),children:w.jsxs("div",{className:Xe("me-[2px] border border-transparent hover:bg-[#F3F5F9] rounded-[6px] size-[25px] flex items-center justify-center",P&&"!bg-[#f5f6f8]"),children:[!M&&w.jsx("img",{src:"icons/edit.svg",alt:"",className:Xe("h-[14px] w-[14px]")}),M&&w.jsx(Vv,{className:On("size-[18px]")})]})}),w.jsxs(AE,{side:"top",align:"start",className:"max-w-[480px] -ms-[10px] flex flex-col gap-[8px] py-[14px] px-[10px] border-none [box-shadow:_0px_8px_20px_-8px_#00000012] rounded-[8px]",children:[w.jsxs(rh,{tabIndex:0,onClick:()=>F(!1),className:Xe("gap-0 cursor-pointer font-normal text-[14px] px-[10px] font-inter capitalize hover:!bg-[#FAF9FF]",!M&&"!bg-[#f5f6f8] hover:!bg-[#f5f6f8]"),children:[w.jsx("img",{src:"icons/edit.svg",alt:"",className:Xe("me-[8px] size-[15px]")}),"Direct (No Moderation)"]}),w.jsxs(rh,{tabIndex:0,onClick:()=>F(!0),className:Xe("gap-0 !cursor-pointer font-normal text-[14px] items-start px-[10px] font-inter hover:!bg-[#FAF9FF]",M&&"!bg-[#f5f6f8] hover:!bg-[#f5f6f8]"),children:[w.jsx(Vv,{className:"me-[8px] !size-[17px] mt-[3px]"}),w.jsxs("div",{children:[w.jsx("div",{children:"Content Moderation"}),w.jsx("small",{className:"font-light",children:"Messages will be flagged for harmful or illicit content and censored accordingly. The agent will see such messages were sent and the reason why they were censored, but it won't see their content."})]})]})]})]}),w.jsx(ip,{role:"textbox",ref:n,placeholder:"Message...",value:i,onKeyDown:Qe,onChange:Re=>s(Re.target.value),rows:1,className:"box-shadow-none placeholder:text-[#282828] resize-none border-none h-full rounded-none min-h-[unset] p-0 whitespace-nowrap no-scrollbar font-inter font-light text-[16px] leading-[100%] bg-white"}),w.jsx(An,{variant:"ghost","data-testid":"submit-button",className:"max-w-[60px] rounded-full hover:bg-white",ref:e,disabled:!i?.trim()||!U?.id,onClick:()=>ye(i),children:w.jsx("img",{src:"icons/send.svg",alt:"Send",height:19.64,width:21.52,className:"h-10"})})]}),w.jsx(Qa,{})]}),w.jsxs("div",{className:"w-full",children:[w.jsx(Qa,{}),w.jsx("div",{}),w.jsx(Qa,{})]})]})})}),w.jsx(HA,{component:w.jsx("div",{className:"flex h-full min-w-[50%] justify-center items-center text-[20px]",children:"Failed to load logs"}),children:w.jsx("div",{className:Xe("fixed top-0 left-[unset] h-full right-0 z-[99] bg-white translate-x-[100%] max-w-[min(700px,35vw)] [box-shadow:0px_0px_30px_0px_#0000001F] w-[min(700px,35vw)] [transition-duration:600ms]",I&&"translate-x-0"),children:I&&w.jsx(wX,{flaggedChanged:()=>{ee(Re=>!Re)},sameTraceMessages:rt.filter(Re=>Re.trace_id===I.trace_id),event:I,regenerateMessageFn:I?.index?Yt(I.index):void 0,resendMessageFn:I?.index||I?.index===0?nt(I.index):void 0,closeLogs:()=>D(null)})})})]})})},CX=T.createContext({}),AX=()=>{const[t,e]=T.useState("");return w.jsxs("div",{className:"bg-white [box-shadow:0px_0px_25px_0px_#0000000A] h-full rounded-[16px] overflow-hidden border-solid w-[352px] min-w-[352px] max-mobile:hidden z-[11] ",children:[w.jsx(FA,{setFilterSessionVal:e,filterSessionVal:t}),w.jsx(UA,{filterSessionVal:t})]})};function kX(){const[t,e]=T.useState(""),{openDialog:n,DialogComponent:r,closeDialog:i}=zA(),[s,o]=T.useState(!1),[l]=lt(rp),[c,d]=lt(As),[,f]=lt(ws),[p,m]=T.useState(""),[,g]=lt(oa),[x]=lt(ws);T.useEffect(()=>{l&&o(!!l.length),setTimeout(()=>{o(!0)},500)},[l]),T.useEffect(()=>{if(c?.id)if(c?.id===ah)e("Parlant | New Session");else{const S=c?.title;S&&e(`Parlant | ${S}`)}else e("Parlant")},[c?.id]),T.useEffect(()=>{f({openDialog:n,closeDialog:i})},[]);const v=()=>{d(null),g(null),x.openDialog("",w.jsx(RA,{}),{height:"536px",width:"604px"})};return w.jsxs(HA,{children:[w.jsxs(CX.Provider,{value:{},children:[w.jsx(V0,{defaultTitle:`${t}`}),w.jsxs("div",{"data-testid":"chatbot",className:"main bg-green-light h-screen flex flex-col rounded-[16px]",children:[w.jsx("div",{className:"hidden max-mobile:block rounded-[16px]",children:w.jsx(FA,{setFilterSessionVal:m,filterSessionVal:p})}),w.jsxs("div",{className:Xe("flex bg-green-light flex-1 gap-[14px] w-full overflow-auto flex-row py-[14px] px-[14px]"),children:[w.jsx(AX,{}),c?.id?w.jsx("div",{className:"h-full w-[calc(100vw-352px-40px)] bg-white rounded-[16px] max-w-[calc(100vw-352px-40px)] max-[800px]:max-w-full max-[800px]:w-full ",children:w.jsx(_X,{})}):w.jsxs("div",{className:"flex-1 flex flex-col gap-[27px] items-center justify-center",children:[w.jsx("img",{className:"pointer-events-none",src:"select-session.svg",fetchPriority:"high",alt:""}),w.jsxs("div",{className:"text-[#3C8C71] select-none font-light text-[18px] flex flex-col gap-[10px] items-center",children:[s&&!l.length?"Start a session to begin chatting":"Select or start a session to begin chatting",w.jsxs("div",{className:"group",children:[w.jsx("img",{src:"buttons/new-session.svg",alt:"add session",className:"shadow-main cursor-pointer group-hover:hidden w-[76px]",tabIndex:1,role:"button",onKeyDown:vs,onClick:v}),w.jsx("img",{src:"buttons/new-session-hover.svg",alt:"add session",className:"shadow-main cursor-pointer hidden group-hover:block w-[76px]",tabIndex:1,role:"button",onKeyDown:vs,onClick:v})]})]})]})]})]})]}),w.jsx(r,{})]})}const NX=(t,e,n,r)=>{const[i,s]=T.useState(!1),[o,l]=T.useState(null),[c,d]=T.useState(!1),f=T.useRef(null),p=T.useCallback(v=>{f.current&&f.current.readyState===WebSocket.OPEN?f.current.send(v):console.warn("WebSocket is not open. Unable to send message:",v)},[]),m=()=>{g(),setTimeout(()=>{(!f?.current?.readyState||!{[f.current.OPEN]:!0,[f.current?.CONNECTING]:!0}[f.current.readyState])&&m()},5e3)};T.useEffect(()=>{g()},[]);const g=T.useCallback(()=>{if(c||f.current?.readyState!=null&&{[f.current.OPEN]:!0,[f.current?.CONNECTING]:!1}[f.current.readyState]){console.warn("WebSocket is already running.");return}(f.current&&f.current.readyState===f.current.OPEN||f.current&&f.current.readyState===f.current?.CONNECTING)&&f.current.close();const v=new WebSocket(t);f.current=v,v.addEventListener("open",()=>{s(!0)}),v.addEventListener("message",S=>{const C=JSON.parse(S.data||"{}");l(S.data),r?.(C)}),v.addEventListener("error",S=>{console.error("WebSocket error:",S)}),v.addEventListener("close",S=>{console.info("WebSocket closed:",S),s(!1),setTimeout(()=>{f?.current?.readyState===0||f?.current?.readyState===1||m()},5e3)}),d(!0)},[t,n,c]),x=T.useCallback(()=>{f.current&&(f.current.close(),f.current=null),s(!1),d(!1)},[]);return T.useEffect(()=>()=>{f.current&&f.current.close()},[]),{isConnected:i,lastMessage:o,sendMessage:p,start:g,pause:x,isRunning:c}},RX=()=>(NX(`${lo}/logs`,!0,null,iG),w.jsx("div",{}));function IX(){return w.jsxs("div",{className:"bg-green-light",children:[w.jsx(kX,{}),w.jsx(RX,{})]})}var QS=["light","dark"],OX="(prefers-color-scheme: dark)",MX=T.createContext(void 0),DX={setTheme:t=>{},themes:[]},LX=()=>{var t;return(t=T.useContext(MX))!=null?t:DX};T.memo(({forcedTheme:t,storageKey:e,attribute:n,enableSystem:r,enableColorScheme:i,defaultTheme:s,value:o,attrs:l,nonce:c})=>{let d=s==="system",f=n==="class"?`var d=document.documentElement,c=d.classList;${`c.remove(${l.map(x=>`'${x}'`).join(",")})`};`:`var d=document.documentElement,n='${n}',s='setAttribute';`,p=i?QS.includes(s)&&s?`if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${s}'`:"if(e==='light'||e==='dark')d.style.colorScheme=e":"",m=(x,v=!1,S=!0)=>{let C=o?o[x]:x,A=v?x+"|| ''":`'${C}'`,k="";return i&&S&&!v&&QS.includes(x)&&(k+=`d.style.colorScheme = '${x}';`),n==="class"?v||C?k+=`c.add(${A})`:k+="null":C&&(k+=`d[s](n,${A})`),k},g=t?`!function(){${f}${m(t)}}()`:r?`!function(){try{${f}var e=localStorage.getItem('${e}');if('system'===e||(!e&&${d})){var t='${OX}',m=window.matchMedia(t);if(m.media!==t||m.matches){${m("dark")}}else{${m("light")}}}else if(e){${o?`var x=${JSON.stringify(o)};`:""}${m(o?"x[e]":"e",!0)}}${d?"":"else{"+m(s,!1,!1)+"}"}${p}}catch(e){}}()`:`!function(){try{${f}var e=localStorage.getItem('${e}');if(e){${o?`var x=${JSON.stringify(o)};`:""}${m(o?"x[e]":"e",!0)}}else{${m(s,!1,!1)};}${p}}catch(t){}}();`;return T.createElement("script",{nonce:c,dangerouslySetInnerHTML:{__html:g}})});const PX=({...t})=>{const{theme:e="system"}=LX();return w.jsx(bD,{theme:e,className:"toaster group",toastOptions:{classNames:{toast:"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",description:"group-[.toast]:text-muted-foreground",actionButton:"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",cancelButton:"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground"}},...t})};jM.createRoot(document.getElementById("root")).render(w.jsxs(T.StrictMode,{children:[w.jsx(IX,{}),w.jsx(PX,{position:"bottom-center",toastOptions:{className:"rounded-full w-fit px-[34px] !bg-[#006E54] text-white"},className:"mb-[80px] transition-none animate-none rounded-full"})]})); ================================================ FILE: src/parlant/api/chat/dist/assets/index-BRVifGSy.css ================================================ @import"https://fonts.googleapis.com/css2?family=Ubuntu+Sans:ital,wght@0,100..800;1,100..800&display=swap";#root{height:100vh;margin:auto;font-family:Inter}body{pointer-events:all!important}.fixed-scroll{overflow:scroll;scrollbar-width:thin;scrollbar-color:#ebecf0 transparent}.fixed-scroll:hover{scrollbar-color:#cdcdcd transparent}.fixed-scroll::-webkit-scrollbar{width:10px}.fixed-scroll::-webkit-scrollbar-thumb{background-color:#00000080;border-radius:10px}.fixed-scroll::-webkit-scrollbar-track{background:transparent}.markdown *{font-size:revert;font-weight:revert;padding:revert;margin:revert;list-style-type:revert;color:revert;-webkit-text-decoration:revert;text-decoration:revert}img{-webkit-user-select:none;-moz-user-select:none;user-select:none}.bubblesWrapper{height:-moz-fit-content;height:fit-content;width:-moz-fit-content;width:fit-content;background-color:#f5f9f7;padding:10px;margin:10px;margin-inline-start:20px;border-radius:15px}.bubbles{height:15px;width:31px;aspect-ratio:2.5;--_g: no-repeat radial-gradient(farthest-side, #333333 90%, #0000);background:var(--_g),var(--_g),var(--_g);background-size:25% 50%;animation:l43 1s infinite linear}@keyframes l43{0%{background-position:0% 50%,50% 50%,100% 50%}20%{background-position:0% 0,50% 50%,100% 50%}40%{background-position:0% 100%,50% 0,100% 50%}60%{background-position:0% 50%,50% 100%,100% 0}80%{background-position:0% 50%,50% 50%,100% 100%}to{background-position:0% 50%,50% 50%,100% 50%}}@keyframes animate-slide-down{0%{max-height:0}to{max-height:300px;min-height:-moz-fit-content;min-height:fit-content}}@keyframes animate-slide-up{0%{max-height:150px}to{max-height:0}}.animate-slide-down{animation:animate-slide-down .5s ease-out forwards}.animate-slide-up{animation:animate-slide-up .3s linear forwards}._editSession_1nfqv_1{position:relative}._editSession_1nfqv_1:before{content:"";position:absolute;top:50%;left:50%;border:1px solid black;pointer-events:none;box-sizing:border-box;height:calc(100% - 4px);width:calc(100% - 8px);transform:translate(-50%,-50%);border-radius:6px}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! Theme: GitHub Description: Light theme as seen on github.com Author: github.com Maintainer: @Hirse Updated: 2021-05-15 Outdated base version: https://github.com/primer/github-syntax-light Current colors taken from GitHub's CSS */.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#005cc5}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-comment,.hljs-code,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}._pendingVideo_n2mv1_1{clip-path:inset(1px .9px .5px .8px round 50%)}._markdown_n2mv1_5 code{white-space:break-spaces;max-width:100%;word-break:break-word;background:transparent!important;font-size:14px}._markdown_n2mv1_5 p{word-break:break-word}._markdown_n2mv1_5 ul{all:revert;margin:0;padding:0;list-style:inside}._markdown_n2mv1_5 h2{font-weight:700}._markdown_n2mv1_5 table{white-space:nowrap;display:block;overflow:scroll;scrollbar-width:auto;border-radius:2px}._markdown_n2mv1_5 table th,._markdown_n2mv1_5 table td{padding-inline:10px;text-align:start}._markdown_n2mv1_5 table th{padding:10px}._markdown_n2mv1_5 table tr:last-child td{padding-bottom:10px}._markdown_n2mv1_5 table thead{border:1px solid lightgray;border-bottom:none;border-radius:3px 3px 0 0;padding:10px}._markdown_n2mv1_5 table tbody{border:1px solid lightgray;border-top:none;border-radius:0 0 3px 3px;padding:10px}._markdown_n2mv1_5 li>div{display:inline}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:100;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-Thin.ttf) format("truetype")}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:200;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-ExtraLight.ttf) format("truetype")}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:300;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-Light.ttf) format("truetype")}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:400;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-Regular.ttf) format("truetype")}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:500;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-Medium.ttf) format("truetype")}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:600;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-SemiBold.ttf) format("truetype")}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:700;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-Bold.ttf) format("truetype")}@font-face{font-family:Ubuntu Sans;font-style:normal;font-weight:800;src:url(/chat/fonts/ubuntu-sans/static/UbuntuSans-ExtraBold.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:100;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:200;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:300;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:400;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:500;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:600;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:700;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:800;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf) format("truetype")}@font-face{font-family:Ubuntu Mono;font-style:normal;font-weight:900;src:url(/chat/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:100;src:url(/chat/fonts/Inter/static/Inter_28pt-Thin.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:200;src:url(/chat/fonts/Inter/static/Inter_28pt-Thin.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:300;src:url(/chat/fonts/Inter/static/Inter_28pt-Light.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:400;src:url(/chat/fonts/Inter/static/Inter_28pt-Regular.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:500;src:url(/chat/fonts/Inter/static/Inter_28pt-Medium.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:600;src:url(/chat/fonts/Inter/static/Inter_28pt-SemiBold.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:700;src:url(/chat/fonts/Inter/static/Inter_28pt-Bold.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:800;src:url(/chat/fonts/Inter/static/Inter_28pt-ExtraBold.ttf) format("truetype")}@font-face{font-family:Inter;font-style:normal;font-weight:900;src:url(/chat/fonts/Inter/static/Inter_28pt-ExtraBold.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:100;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Thin.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:200;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Thin.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:300;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Light.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:400;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Regular.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:500;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Medium.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:600;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-SemiBold.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:700;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:800;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf) format("truetype")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:900;src:url(/chat/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf) format("truetype")}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--main: #fbfbfb;--background: 0 0% 100%;--foreground: 0 0% 3.9%;--card: 0 0% 100%;--card-foreground: 0 0% 3.9%;--popover: 0 0% 100%;--popover-foreground: 0 0% 3.9%;--primary: 0 0% 9%;--primary-foreground: 0 0% 98%;--secondary: 0 0% 96.1%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96.1%;--muted-foreground: 0 0% 45.1%;--accent: 0 0% 96.1%;--accent-foreground: 0 0% 9%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 0 0% 98%;--border: 0 0% 89.8%;--input: 0 0% 89.8%;--ring: 0 0% 3.9%;--chart-1: 12 76% 61%;--chart-2: 173 58% 39%;--chart-3: 197 37% 24%;--chart-4: 43 74% 66%;--chart-5: 27 87% 67%;--radius: .5rem}.dark{--main: rgb(1, 37, 21)}*{border-color:hsl(var(--border))}*{border-color:hsl(var(--border));outline-color:hsl(var(--ring) / .5)}body{background-color:hsl(var(--background));color:hsl(var(--foreground))}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:801px){.container{max-width:801px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1080px){.container{max-width:1080px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.\!pointer-events-auto{pointer-events:auto!important}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.-bottom-\[3px\]{bottom:-3px}.-top-\[1px\]{top:-1px}.bottom-0{bottom:0}.bottom-\[-1px\]{bottom:-1px}.left-0{left:0}.left-2{left:.5rem}.left-\[-1px\]{left:-1px}.left-\[24px\]{left:24px}.left-\[34px\]{left:34px}.left-\[50\%\]{left:50%}.left-\[unset\]{left:unset}.right-0{right:0}.right-4{right:1rem}.right-\[-1px\]{right:-1px}.right-\[10px\]{right:10px}.right-\[1px\]{right:1px}.top-0{top:0}.top-4{top:1rem}.top-\[-1px\]{top:-1px}.top-\[10px\]{top:10px}.top-\[38px\]{top:38px}.top-\[50\%\]{top:50%}.top-\[8px\]{top:8px}.z-0{z-index:0}.z-10{z-index:10}.z-50{z-index:50}.z-\[11\]{z-index:11}.z-\[1\]{z-index:1}.z-\[999999\]{z-index:999999}.z-\[999\]{z-index:999}.z-\[99\]{z-index:99}.m-auto{margin:auto}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-0{margin-left:0;margin-right:0}.mx-\[10px\]{margin-left:10px;margin-right:10px}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-\[5px\]{margin-top:5px;margin-bottom:5px}.\!ms-\[12px\]{margin-inline-start:12px!important}.\!mt-0{margin-top:0!important}.-mb-\[10px\]{margin-bottom:-10px}.-mb-\[7px\]{margin-bottom:-7px}.-me-\[6px\]{margin-inline-end:-6px}.-ms-\[10px\]{margin-inline-start:-10px}.mb-1{margin-bottom:.25rem}.mb-\[10px\]{margin-bottom:10px}.mb-\[12px\]{margin-bottom:12px}.mb-\[14px\]{margin-bottom:14px}.mb-\[16px\]{margin-bottom:16px}.mb-\[1px\]{margin-bottom:1px}.mb-\[26px\]{margin-bottom:26px}.mb-\[80px\]{margin-bottom:80px}.me-4{margin-inline-end:1rem}.me-\[10px\]{margin-inline-end:10px}.me-\[14px\]{margin-inline-end:14px}.me-\[18px\]{margin-inline-end:18px}.me-\[20px\]{margin-inline-end:20px}.me-\[2px\]{margin-inline-end:2px}.me-\[30px\]{margin-inline-end:30px}.me-\[3px\]{margin-inline-end:3px}.me-\[5px\]{margin-inline-end:5px}.me-\[6px\]{margin-inline-end:6px}.me-\[8px\]{margin-inline-end:8px}.ml-0{margin-left:0}.ml-\[1px\]{margin-left:1px}.ml-\[23px\]{margin-left:23px}.ml-auto{margin-left:auto}.mr-0{margin-right:0}.ms-\[24px\]{margin-inline-start:24px}.ms-\[30px\]{margin-inline-start:30px}.ms-\[4px\]{margin-inline-start:4px}.ms-\[6px\]{margin-inline-start:6px}.ms-\[8px\]{margin-inline-start:8px}.mt-24{margin-top:6rem}.mt-\[0\]{margin-top:0}.mt-\[10px\]{margin-top:10px}.mt-\[24px\]{margin-top:24px}.mt-\[26px\]{margin-top:26px}.mt-\[30px\]{margin-top:30px}.mt-\[3px\]{margin-top:3px}.mt-\[40px\]{margin-top:40px}.mt-\[46px\]{margin-top:46px}.mt-\[4px\]{margin-top:4px}.mt-\[9px\]{margin-top:9px}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.contents{display:contents}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.\!size-\[17px\]{width:17px!important;height:17px!important}.size-\[14px\]{width:14px;height:14px}.size-\[15px\]{width:15px;height:15px}.size-\[16px\]{width:16px;height:16px}.size-\[18px\]{width:18px;height:18px}.size-\[25px\]{width:25px;height:25px}.size-\[26px\]{width:26px;height:26px}.size-\[28px\]{width:28px;height:28px}.size-\[30px\]{width:30px;height:30px}.size-\[330px\]{width:330px;height:330px}.size-\[36px\]{width:36px;height:36px}.size-\[38px\]{width:38px;height:38px}.size-\[44px\]{width:44px;height:44px}.h-0{height:0px}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-9{height:2.25rem}.h-\[-webkit-fill-available\]{height:-webkit-fill-available}.h-\[100\%\]{height:100%}.h-\[120px\]{height:120px}.h-\[14px\]{height:14px}.h-\[1em\]{height:1em}.h-\[20px\]{height:20px}.h-\[21px\]{height:21px}.h-\[24px\]{height:24px}.h-\[28px\]{height:28px}.h-\[30px\]{height:30px}.h-\[32px\]{height:32px}.h-\[34px\]{height:34px}.h-\[35px\]{height:35px}.h-\[36px\]{height:36px}.h-\[38px\]{height:38px}.h-\[39px\]{height:39px}.h-\[46px\]{height:46px}.h-\[47px\]{height:47px}.h-\[48\.67px\]{height:48.67px}.h-\[48px\]{height:48px}.h-\[54px\]{height:54px}.h-\[58px\]{height:58px}.h-\[60px\]{height:60px}.h-\[70px\]{height:70px}.h-\[74px\]{height:74px}.h-\[78px\]{height:78px}.h-\[80\%\]{height:80%}.h-\[80px\]{height:80px}.h-\[calc\(100\%-12px\)\]{height:calc(100% - 12px)}.h-\[calc\(100\%-4px\)\]{height:calc(100% - 4px)}.h-\[calc\(100\%-60px\)\]{height:calc(100% - 60px)}.h-\[calc\(100\%-68px\)\]{height:calc(100% - 68px)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-96{max-height:24rem}.max-h-\[200px\]{max-height:200px}.max-h-\[28px\]{max-height:28px}.max-h-\[300px\]{max-height:300px}.max-h-\[308px\]{max-height:308px}.max-h-\[30px\]{max-height:30px}.max-h-\[50\%\]{max-height:50%}.min-h-0{min-height:0px}.min-h-\[14px\]{min-height:14px}.min-h-\[26px\]{min-height:26px}.min-h-\[28px\]{min-height:28px}.min-h-\[30px\]{min-height:30px}.min-h-\[40px\]{min-height:40px}.min-h-\[42px\]{min-height:42px}.min-h-\[48px\]{min-height:48px}.min-h-\[50px\]{min-height:50px}.min-h-\[58px\]{min-height:58px}.min-h-\[60px\]{min-height:60px}.min-h-\[70px\]{min-height:70px}.min-h-\[74px\]{min-height:74px}.min-h-\[78px\]{min-height:78px}.min-h-\[80px\]{min-height:80px}.min-h-\[unset\]{min-height:unset}.\!w-fit{width:-moz-fit-content!important;width:fit-content!important}.w-10{width:2.5rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-4{width:1rem}.w-9{width:2.25rem}.w-\[1020px\]{width:1020px}.w-\[12px\]{width:12px}.w-\[130px\]{width:130px}.w-\[14px\]{width:14px}.w-\[161px\]{width:161px}.w-\[168px\]{width:168px}.w-\[16px\]{width:16px}.w-\[246px\]{width:246px}.w-\[24px\]{width:24px}.w-\[28px\]{width:28px}.w-\[2px\]{width:2px}.w-\[352px\]{width:352px}.w-\[36px\]{width:36px}.w-\[560px\]{width:560px}.w-\[600px\]{width:600px}.w-\[68px\]{width:68px}.w-\[73px\]{width:73px}.w-\[76px\]{width:76px}.w-\[79px\]{width:79px}.w-\[84px\]{width:84px}.w-\[86px\]{width:86px}.w-\[95px\]{width:95px}.w-\[96px\]{width:96px}.w-\[calc\(100\%-412px\)\]{width:calc(100% - 412px)}.w-\[calc\(100\%-4px\)\]{width:calc(100% - 4px)}.w-\[calc\(100\%-min\(700px\,35vw\)\)\]{width:calc(100% - min(700px,35vw))}.w-\[calc\(100vw-352px-40px\)\]{width:calc(100vw - 392px)}.w-\[min\(700px\,35vw\)\]{width:min(700px,35vw)}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.min-w-\[12px\]{min-width:12px}.min-w-\[14px\]{min-width:14px}.min-w-\[16px\]{min-width:16px}.min-w-\[18px\]{min-width:18px}.min-w-\[200px\]{min-width:200px}.min-w-\[26px\]{min-width:26px}.min-w-\[28px\]{min-width:28px}.min-w-\[352px\]{min-width:352px}.min-w-\[50\%\]{min-width:50%}.min-w-\[86px\]{min-width:86px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[min\(1000px\,100\%\)\]{min-width:min(1000px,100%)}.min-w-\[min\(560px\,100\%\)\]{min-width:min(560px,100%)}.min-w-\[unset\]{min-width:unset}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.min-w-full{min-width:100%}.min-w-max{min-width:-moz-max-content;min-width:max-content}.max-w-\[-webkit-fill-available\]{max-width:-webkit-fill-available}.max-w-\[1000px\]{max-width:1000px}.max-w-\[200px\]{max-width:200px}.max-w-\[210px\]{max-width:210px}.max-w-\[320px\]{max-width:320px}.max-w-\[378px\]{max-width:378px}.max-w-\[480px\]{max-width:480px}.max-w-\[608px\]{max-width:608px}.max-w-\[60px\]{max-width:60px}.max-w-\[80\%\]{max-width:80%}.max-w-\[80vw\]{max-width:80vw}.max-w-\[90\%\]{max-width:90%}.max-w-\[95\%\]{max-width:95%}.max-w-\[calc\(100\%-25px\)\]{max-width:calc(100% - 25px)}.max-w-\[calc\(100vw-352px-40px\)\]{max-width:calc(100vw - 392px)}.max-w-\[min\(1000px\,100\%\)\]{max-width:min(1000px,100%)}.max-w-\[min\(1020px\,100\%\)\]{max-width:min(1020px,100%)}.max-w-\[min\(560px\,100\%\)\]{max-width:min(560px,100%)}.max-w-\[min\(560px\,90\%\)\]{max-width:min(560px,90%)}.max-w-\[min\(560px\,calc\(100\%-30px\)\)\]{max-width:min(560px,calc(100% - 30px))}.max-w-\[min\(700px\,35vw\)\]{max-width:min(700px,35vw)}.max-w-\[unset\]{max-width:unset}.max-w-fit{max-width:-moz-fit-content;max-width:fit-content}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.\!origin-top{transform-origin:top!important}.origin-bottom{transform-origin:bottom}.-translate-y-\[-50\%\]{--tw-translate-y: 50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-\[70px\]{--tw-translate-y: -70px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[100\%\]{--tw-translate-x: 100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[0px\]{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.animate-background-shift{animation:background-shift 5s linear infinite}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.animate-fade-in{animation:fade-in .3s linear}@keyframes fade-in-fast{0%{opacity:0;transform:translateY(6px) scale(.98);filter:blur(1px)}to{opacity:1;transform:translateY(0) scale(1);filter:blur(0)}}.animate-fade-in-fast{animation:fade-in-fast .4s ease-out}.animate-none{animation:none}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.\!cursor-pointer{cursor:pointer!important}.cursor-auto{cursor:auto}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.\!resize-none{resize:none!important}.resize-none{resize:none}.resize{resize:both}.snap-end{scroll-snap-align:end}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-stretch{align-items:stretch}.\!justify-start{justify-content:flex-start!important}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-\[10px\]{gap:10px}.gap-\[12px\]{gap:12px}.gap-\[14px\]{gap:14px}.gap-\[16px\]{gap:16px}.gap-\[17px\]{gap:17px}.gap-\[20px\]{gap:20px}.gap-\[22px\]{gap:22px}.gap-\[27px\]{gap:27px}.gap-\[3px\]{gap:3px}.gap-\[4px\]{gap:4px}.gap-\[5px\]{gap:5px}.gap-\[6px\]{gap:6px}.gap-\[7px\]{gap:7px}.gap-\[8px\]{gap:8px}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-stretch{align-self:stretch}.justify-self-end{justify-self:end}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-visible{overflow-y:visible}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overflow-ellipsis,.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.text-wrap{text-wrap:wrap}.text-nowrap{text-wrap:nowrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-\[10px\]{border-radius:10px}.rounded-\[12px\]{border-radius:12px}.rounded-\[14px\]{border-radius:14px}.rounded-\[16px\]{border-radius:16px}.rounded-\[20px\]{border-radius:20px}.rounded-\[22px\]{border-radius:22px}.rounded-\[2px\]{border-radius:2px}.rounded-\[3px\]{border-radius:3px}.rounded-\[4px\]{border-radius:4px}.rounded-\[5px\]{border-radius:5px}.rounded-\[6\.5px\]{border-radius:6.5px}.rounded-\[6px\]{border-radius:6px}.rounded-\[7px\]{border-radius:7px}.rounded-\[8px\]{border-radius:8px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-s-\[16px\]{border-start-start-radius:16px;border-end-start-radius:16px}.rounded-br-none{border-bottom-right-radius:0}.rounded-ee-\[16px\]{border-end-end-radius:16px}.rounded-es-\[16px\]{border-end-start-radius:16px}.rounded-se-\[16px\]{border-start-end-radius:16px}.rounded-ss-\[16px\]{border-start-start-radius:16px}.\!border-0{border-width:0px!important}.border{border-width:1px}.border-2{border-width:2px}.border-\[0\.6px\]{border-width:.6px}.border-\[2px\]{border-width:2px}.border-\[6px\]{border-width:6px}.border-y-\[10px\]{border-top-width:10px;border-bottom-width:10px}.border-b{border-bottom-width:1px}.border-b-\[0\.6px\]{border-bottom-width:.6px}.border-b-\[12px\]{border-bottom-width:12px}.border-e{border-inline-end-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0px}.border-solid{border-style:solid}.border-none{border-style:none}.\!border-black{--tw-border-opacity: 1 !important;border-color:rgb(0 0 0 / var(--tw-border-opacity, 1))!important}.\!border-transparent{border-color:transparent!important}.border-\[\#9B0360\]{--tw-border-opacity: 1;border-color:rgb(155 3 96 / var(--tw-border-opacity, 1))}.border-\[\#EBECF0\]{--tw-border-opacity: 1;border-color:rgb(235 236 240 / var(--tw-border-opacity, 1))}.border-\[\#EDEFF3\]{--tw-border-opacity: 1;border-color:rgb(237 239 243 / var(--tw-border-opacity, 1))}.border-\[\#EEEEEE\]{--tw-border-opacity: 1;border-color:rgb(238 238 238 / var(--tw-border-opacity, 1))}.border-\[\#F3F5F9\]{--tw-border-opacity: 1;border-color:rgb(243 245 249 / var(--tw-border-opacity, 1))}.border-\[\#F5F9F7\]{--tw-border-opacity: 1;border-color:rgb(245 249 247 / var(--tw-border-opacity, 1))}.border-\[\#F9FAFC\]{--tw-border-opacity: 1;border-color:rgb(249 250 252 / var(--tw-border-opacity, 1))}.border-\[\#dedcdc\]{--tw-border-opacity: 1;border-color:rgb(222 220 220 / var(--tw-border-opacity, 1))}.border-\[\#ebecf0\]{--tw-border-opacity: 1;border-color:rgb(235 236 240 / var(--tw-border-opacity, 1))}.border-\[\#eeeeee\]{--tw-border-opacity: 1;border-color:rgb(238 238 238 / var(--tw-border-opacity, 1))}.border-black{--tw-border-opacity: 1;border-color:rgb(0 0 0 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-muted{--tw-border-opacity: 1;border-color:rgb(235 236 240 / var(--tw-border-opacity, 1))}.border-primary{border-color:hsl(var(--primary))}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.border-b-\[\#EBECF0\],.border-b-\[\#ebecf0\]{--tw-border-opacity: 1;border-bottom-color:rgb(235 236 240 / var(--tw-border-opacity, 1))}.\!bg-\[\#006E54\]{--tw-bg-opacity: 1 !important;background-color:rgb(0 110 84 / var(--tw-bg-opacity, 1))!important}.\!bg-\[\#F5F6F8\]{--tw-bg-opacity: 1 !important;background-color:rgb(245 246 248 / var(--tw-bg-opacity, 1))!important}.\!bg-\[\#FAFAFA\]{--tw-bg-opacity: 1 !important;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))!important}.\!bg-\[\#FDF2F1\]{--tw-bg-opacity: 1 !important;background-color:rgb(253 242 241 / var(--tw-bg-opacity, 1))!important}.\!bg-\[\#f5f6f8\]{--tw-bg-opacity: 1 !important;background-color:rgb(245 246 248 / var(--tw-bg-opacity, 1))!important}.\!bg-gray-300{--tw-bg-opacity: 1 !important;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))!important}.\!bg-gray-4{--tw-bg-opacity: 1 !important;background-color:rgb(245 246 248 / var(--tw-bg-opacity, 1))!important}.\!bg-white{--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))!important}.bg-\[\#006E53\]{--tw-bg-opacity: 1;background-color:rgb(0 110 83 / var(--tw-bg-opacity, 1))}.bg-\[\#EBECF0\]{--tw-bg-opacity: 1;background-color:rgb(235 236 240 / var(--tw-bg-opacity, 1))}.bg-\[\#F2F0FC\]{--tw-bg-opacity: 1;background-color:rgb(242 240 252 / var(--tw-bg-opacity, 1))}.bg-\[\#F5F6F8\]{--tw-bg-opacity: 1;background-color:rgb(245 246 248 / var(--tw-bg-opacity, 1))}.bg-\[\#F5F9F7\]{--tw-bg-opacity: 1;background-color:rgb(245 249 247 / var(--tw-bg-opacity, 1))}.bg-\[\#FAFAFA\]{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.bg-\[\#FBFBFB\]{--tw-bg-opacity: 1;background-color:rgb(251 251 251 / var(--tw-bg-opacity, 1))}.bg-\[\#ebecf0\]{--tw-bg-opacity: 1;background-color:rgb(235 236 240 / var(--tw-bg-opacity, 1))}.bg-\[\#f0eeee\]{--tw-bg-opacity: 1;background-color:rgb(240 238 238 / var(--tw-bg-opacity, 1))}.bg-\[\#f5f5f9\]{--tw-bg-opacity: 1;background-color:rgb(245 245 249 / var(--tw-bg-opacity, 1))}.bg-\[\#f5f6f8\]{--tw-bg-opacity: 1;background-color:rgb(245 246 248 / var(--tw-bg-opacity, 1))}.bg-background{background-color:hsl(var(--background))}.bg-black\/80{background-color:#000c}.bg-border{background-color:hsl(var(--border))}.bg-current{background-color:currentColor}.bg-destructive{background-color:hsl(var(--destructive))}.bg-gray-1{--tw-bg-opacity: 1;background-color:rgb(169 169 169 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-3{--tw-bg-opacity: 1;background-color:rgb(235 236 240 / var(--tw-bg-opacity, 1))}.bg-green-light{--tw-bg-opacity: 1;background-color:rgb(245 249 247 / var(--tw-bg-opacity, 1))}.bg-green-main{--tw-bg-opacity: 1;background-color:rgb(0 110 83 / var(--tw-bg-opacity, 1))}.bg-main{background-color:var(--main)}.bg-muted{--tw-bg-opacity: 1;background-color:rgb(235 236 240 / var(--tw-bg-opacity, 1))}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-secondary{background-color:hsl(var(--secondary))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.fill-current{fill:currentColor}.\!p-\[4px_2px\]{padding:4px 2px!important}.p-0{padding:0}.p-1{padding:.25rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[0\.9rem\]{padding:.9rem}.p-\[10px\]{padding:10px}.p-\[13px_22px_17px_22px\]{padding:13px 22px 17px}.p-\[14px\]{padding:14px}.p-\[16px\]{padding:16px}.p-\[20px\]{padding:20px}.p-\[20px_22px_24px_22px\]{padding:20px 22px 24px}.p-\[5px\]{padding:5px}.p-\[5px_16px_7px_16px\]{padding:5px 16px 7px}.p-\[6px\]{padding:6px}.p-\[8px\]{padding:8px}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-\[\.25em\]{padding-left:.25em;padding-right:.25em}.px-\[10px\]{padding-left:10px;padding-right:10px}.px-\[12px\]{padding-left:12px;padding-right:12px}.px-\[14px\]{padding-left:14px;padding-right:14px}.px-\[20px\]{padding-left:20px;padding-right:20px}.px-\[22px\]{padding-left:22px;padding-right:22px}.px-\[24px\]{padding-left:24px;padding-right:24px}.px-\[29\.5px\]{padding-left:29.5px;padding-right:29.5px}.px-\[2px\]{padding-left:2px;padding-right:2px}.px-\[34px\]{padding-left:34px;padding-right:34px}.px-\[39px\]{padding-left:39px;padding-right:39px}.px-\[8px\]{padding-left:8px;padding-right:8px}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-\[10px\]{padding-top:10px;padding-bottom:10px}.py-\[12px\]{padding-top:12px;padding-bottom:12px}.py-\[13px\]{padding-top:13px;padding-bottom:13px}.py-\[14px\]{padding-top:14px;padding-bottom:14px}.py-\[20px\]{padding-top:20px;padding-bottom:20px}.py-\[3px\]{padding-top:3px;padding-bottom:3px}.py-\[42px\]{padding-top:42px;padding-bottom:42px}.py-\[4px\]{padding-top:4px;padding-bottom:4px}.py-\[5px\]{padding-top:5px;padding-bottom:5px}.py-\[8px\]{padding-top:8px;padding-bottom:8px}.pb-0{padding-bottom:0}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-\[11px\]{padding-bottom:11px}.pb-\[14px\]{padding-bottom:14px}.pb-\[2px\]{padding-bottom:2px}.pb-\[4px\]{padding-bottom:4px}.pb-\[5px\]{padding-bottom:5px}.pb-\[8px\]{padding-bottom:8px}.pe-0{padding-inline-end:0px}.pe-\[108px\]{padding-inline-end:108px}.pe-\[10px\]{padding-inline-end:10px}.pe-\[12px\]{padding-inline-end:12px}.pe-\[14px\]{padding-inline-end:14px}.pe-\[18px\]{padding-inline-end:18px}.pe-\[20px\]{padding-inline-end:20px}.pe-\[38px\]{padding-inline-end:38px}.pe-\[6px\]{padding-inline-end:6px}.pe-\[8px\]{padding-inline-end:8px}.pl-8{padding-left:2rem}.pr-2{padding-right:.5rem}.ps-\[10px\]{padding-inline-start:10px}.ps-\[12px\]{padding-inline-start:12px}.ps-\[14px\]{padding-inline-start:14px}.ps-\[15px\]{padding-inline-start:15px}.ps-\[20px\]{padding-inline-start:20px}.ps-\[22px\]{padding-inline-start:22px}.ps-\[30px\]{padding-inline-start:30px}.ps-\[35px\]{padding-inline-start:35px}.ps-\[4px\]{padding-inline-start:4px}.ps-\[5px\]{padding-inline-start:5px}.ps-\[6px\]{padding-inline-start:6px}.ps-\[8px\]{padding-inline-start:8px}.pt-0{padding-top:0}.pt-\[10px\]{padding-top:10px}.pt-\[11px\]{padding-top:11px}.pt-\[1px\]{padding-top:1px}.pt-\[4px\]{padding-top:4px}.pt-\[6px\]{padding-top:6px}.text-center{text-align:center}.align-text-bottom{vertical-align:text-bottom}.font-ibm-plex-mono{font-family:IBM Plex Mono}.font-inter{font-family:inter}.font-ubuntu-mono{font-family:Ubuntu Mono}.\!text-\[13px\]{font-size:13px!important}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[18px\]{font-size:18px}.text-\[20px\]{font-size:20px}.text-\[8px\]{font-size:8px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.leading-\[100\%\]{line-height:100%}.leading-\[14px\]{line-height:14px}.leading-\[16px\]{line-height:16px}.leading-\[18px\]{line-height:18px}.leading-\[19px\]{line-height:19px}.leading-\[26px\]{line-height:26px}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-widest{letter-spacing:.1em}.\!text-\[\#282828\]{--tw-text-opacity: 1 !important;color:rgb(40 40 40 / var(--tw-text-opacity, 1))!important}.\!text-\[\#9B0360\]{--tw-text-opacity: 1 !important;color:rgb(155 3 96 / var(--tw-text-opacity, 1))!important}.\!text-\[\#a9a9a9\]{--tw-text-opacity: 1 !important;color:rgb(169 169 169 / var(--tw-text-opacity, 1))!important}.\!text-white{--tw-text-opacity: 1 !important;color:rgb(255 255 255 / var(--tw-text-opacity, 1))!important}.text-\[\#151515\]{--tw-text-opacity: 1;color:rgb(21 21 21 / var(--tw-text-opacity, 1))}.text-\[\#282828\]{--tw-text-opacity: 1;color:rgb(40 40 40 / var(--tw-text-opacity, 1))}.text-\[\#333\]{--tw-text-opacity: 1;color:rgb(51 51 51 / var(--tw-text-opacity, 1))}.text-\[\#3C8C71\]{--tw-text-opacity: 1;color:rgb(60 140 113 / var(--tw-text-opacity, 1))}.text-\[\#656565\]{--tw-text-opacity: 1;color:rgb(101 101 101 / var(--tw-text-opacity, 1))}.text-\[\#777\]{--tw-text-opacity: 1;color:rgb(119 119 119 / var(--tw-text-opacity, 1))}.text-\[\#959595\]{--tw-text-opacity: 1;color:rgb(149 149 149 / var(--tw-text-opacity, 1))}.text-\[\#A9A9A9\]{--tw-text-opacity: 1;color:rgb(169 169 169 / var(--tw-text-opacity, 1))}.text-\[\#A9AFB7\]{--tw-text-opacity: 1;color:rgb(169 175 183 / var(--tw-text-opacity, 1))}.text-\[\#AEB4BB\]{--tw-text-opacity: 1;color:rgb(174 180 187 / var(--tw-text-opacity, 1))}.text-\[\#CDCDCD\]{--tw-text-opacity: 1;color:rgb(205 205 205 / var(--tw-text-opacity, 1))}.text-\[\#a9a9a9\]{--tw-text-opacity: 1;color:rgb(169 169 169 / var(--tw-text-opacity, 1))}.text-\[\#ef5350\]{--tw-text-opacity: 1;color:rgb(239 83 80 / var(--tw-text-opacity, 1))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-current{color:currentColor}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--foreground))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-\[0\.3\]{opacity:.3}.opacity-\[33\%\]{opacity:33%}.\!shadow-main{--tw-shadow: 0 3px 3px 0 #00000005 !important;--tw-shadow-colored: 0 3px 3px 0 var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.\!shadow-none{--tw-shadow: 0 0 #0000 !important;--tw-shadow-colored: 0 0 #0000 !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-main{--tw-shadow: 0 3px 3px 0 #00000005;--tw-shadow-colored: 0 3px 3px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-main-inset{--tw-shadow: 0px 0px 3px 0px #00000054 inset;--tw-shadow-colored: inset 0px 0px 3px 0px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.\!shadow-main{--tw-shadow-color: var(--main) !important;--tw-shadow: var(--tw-shadow-colored) !important}.shadow-main{--tw-shadow-color: var(--main);--tw-shadow: var(--tw-shadow-colored) }.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.\!ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color) !important;--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color) !important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)!important}.ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.\!ring-offset-0{--tw-ring-offset-width: 0px !important}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background)) }.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-\[4px\]{--tw-blur: blur(4px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-none{transition-property:none}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial }.fade-in-0{--tw-enter-opacity: 0 }.zoom-in-95{--tw-enter-scale: .95 }.duration-200{animation-duration:.2s}.duration-500{animation-duration:.5s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.running{animation-play-state:running}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}.box-shadow-none{box-shadow:none!important}.\[box-shadow\:0_-0\.6px_0px_0px_\#F3F5F9\]{box-shadow:0 -.6px #f3f5f9}.\[box-shadow\:0px_0px_25px_0px_\#0000000A\]{box-shadow:0 0 25px #0000000a}.\[box-shadow\:0px_0px_30px_0px_\#0000001F\]{box-shadow:0 0 30px #0000001f}.\[box-shadow\:0px_2px_4px_0px_\#00403029\,0px_1px_5\.5px_0px_\#006E5329\]{box-shadow:0 2px 4px #00403029,0 1px 5.5px #006e5329}.\[box-shadow\:_0px_8px_20px_-8px_\#00000012\]{box-shadow:0 8px 20px -8px #00000012}.\[direction\:ltr\]{direction:ltr}.\[direction\:rtl\]{direction:rtl}.\[justify-self\:end\]{justify-self:end}.\[scroll-snap-type\:y_mandatory\]{scroll-snap-type:y mandatory}.\[stroke-width\:2px\]{stroke-width:2px}.\[transition-duration\:600ms\]{transition-duration:.6s}.\[word-break\:break-all\]{word-break:break-all}.\[word-break\:break-word\]{word-break:break-word}*{--tw-ring-offset-shadow: transparent !important;--tw-ring-shadow: transparent !important;--tw-shadow: transparent !important}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::file-selector-button{color:hsl(var(--foreground))}.placeholder\:font-light::-moz-placeholder{font-weight:300}.placeholder\:font-light::placeholder{font-weight:300}.placeholder\:text-\[\#282828\]::-moz-placeholder{--tw-text-opacity: 1;color:rgb(40 40 40 / var(--tw-text-opacity, 1))}.placeholder\:text-\[\#282828\]::placeholder{--tw-text-opacity: 1;color:rgb(40 40 40 / var(--tw-text-opacity, 1))}.placeholder\:text-\[\#959595\]::-moz-placeholder{--tw-text-opacity: 1;color:rgb(149 149 149 / var(--tw-text-opacity, 1))}.placeholder\:text-\[\#959595\]::placeholder{--tw-text-opacity: 1;color:rgb(149 149 149 / var(--tw-text-opacity, 1))}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-y-0:after{content:var(--tw-content);top:0;bottom:0}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-1:after{content:var(--tw-content);width:.25rem}.after\:-translate-x-1\/2:after{content:var(--tw-content);--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.focus-within\:\!bg-white:focus-within{--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))!important}.hover\:visible:hover{visibility:visible}@keyframes background-shift{0%,to{background-position-x:20%}50%{background-position-x:80%}}.hover\:animate-background-shift:hover{animation:background-shift 5s linear infinite}.hover\:cursor-text:hover{cursor:text}.hover\:rounded-\[6px\]:hover{border-radius:6px}.hover\:border-\[\#E4E6EA\]:hover{--tw-border-opacity: 1;border-color:rgb(228 230 234 / var(--tw-border-opacity, 1))}.hover\:border-\[\#E9EBEF\]:hover{--tw-border-opacity: 1;border-color:rgb(233 235 239 / var(--tw-border-opacity, 1))}.hover\:\!bg-\[\#F5EFEF\]:hover{--tw-bg-opacity: 1 !important;background-color:rgb(245 239 239 / var(--tw-bg-opacity, 1))!important}.hover\:\!bg-\[\#FAF9FF\]:hover{--tw-bg-opacity: 1 !important;background-color:rgb(250 249 255 / var(--tw-bg-opacity, 1))!important}.hover\:\!bg-\[\#f5f6f8\]:hover{--tw-bg-opacity: 1 !important;background-color:rgb(245 246 248 / var(--tw-bg-opacity, 1))!important}.hover\:\!bg-white:hover{--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))!important}.hover\:bg-\[\#005C3F\]:hover{--tw-bg-opacity: 1;background-color:rgb(0 92 63 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#EBE9F5\]:hover{--tw-bg-opacity: 1;background-color:rgb(235 233 245 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#EBECF0\]:hover{--tw-bg-opacity: 1;background-color:rgb(235 236 240 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#F3F5F9\]:hover{--tw-bg-opacity: 1;background-color:rgb(243 245 249 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#F5F6F8\]:hover{--tw-bg-opacity: 1;background-color:rgb(245 246 248 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#F5F9F3\]:hover{--tw-bg-opacity: 1;background-color:rgb(245 249 243 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#FAFAFA\]:hover{--tw-bg-opacity: 1;background-color:rgb(250 250 250 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#FBFBFB\]:hover{--tw-bg-opacity: 1;background-color:rgb(251 251 251 / var(--tw-bg-opacity, 1))}.hover\:bg-\[\#f3f5f9\]:hover{--tw-bg-opacity: 1;background-color:rgb(243 245 249 / var(--tw-bg-opacity, 1))}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-green-hover:hover{--tw-bg-opacity: 1;background-color:rgb(0 92 63 / var(--tw-bg-opacity, 1))}.hover\:bg-main:hover{background-color:var(--main)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:bg-white:hover{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.hover\:text-\[\#151515\]:hover{--tw-text-opacity: 1;color:rgb(21 21 21 / var(--tw-text-opacity, 1))}.hover\:text-\[\#282828\]:hover{--tw-text-opacity: 1;color:rgb(40 40 40 / var(--tw-text-opacity, 1))}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:\!bg-white:focus{--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))!important}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring)) }.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px }.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring)) }.focus-visible\:ring-offset-1:focus-visible{--tw-ring-offset-width: 1px }.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px }.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: hsl(var(--background)) }.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group\/main:hover .group-hover\/main\:visible,.group:hover .group-hover\:visible{visibility:visible}.group:hover .group-hover\:block{display:block}.group:hover .group-hover\:flex{display:flex}.group:hover .group-hover\:hidden{display:none}.group:hover .group-hover\:border-\[\#FAFAFA\]{--tw-border-opacity: 1;border-color:rgb(250 250 250 / var(--tw-border-opacity, 1))}.group:hover .group-hover\:bg-\[\#EBECF0\]{--tw-bg-opacity: 1;background-color:rgb(235 236 240 / var(--tw-bg-opacity, 1))}.group:hover .group-hover\:text-\[\#656565\]{--tw-text-opacity: 1;color:rgb(101 101 101 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:underline{text-decoration-line:underline}.group.toaster .group-\[\.toaster\]\:border-border{border-color:hsl(var(--border))}.group.toast .group-\[\.toast\]\:bg-muted{--tw-bg-opacity: 1;background-color:rgb(235 236 240 / var(--tw-bg-opacity, 1))}.group.toast .group-\[\.toast\]\:bg-primary{background-color:hsl(var(--primary))}.group.toaster .group-\[\.toaster\]\:bg-background{background-color:hsl(var(--background))}.group.toast .group-\[\.toast\]\:text-primary-foreground{color:hsl(var(--primary-foreground))}.group.toaster .group-\[\.toaster\]\:text-foreground{color:hsl(var(--foreground))}.group.toaster .group-\[\.toaster\]\:shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.peer:hover~.peer-hover\:visible{visibility:visible}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction=vertical]{height:1px}.data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction=vertical]{width:100%}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction=vertical]{flex-direction:column}.data-\[selected\=true\]\:bg-accent[data-selected=true]{background-color:hsl(var(--accent))}.data-\[state\=checked\]\:bg-green-main[data-state=checked]{--tw-bg-opacity: 1;background-color:rgb(0 110 83 / var(--tw-bg-opacity, 1))}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:hsl(var(--secondary))}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:hsl(var(--primary-foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=closed\]\:duration-300[data-state=closed]{transition-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{transition-duration:.5s}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial }.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial }.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0 }.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0 }.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95 }.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95 }.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem }.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem }.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem }.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem }.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y: 100% }.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x: -100% }.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50% }.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x: 100% }.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y: -100% }.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48% }.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y: 100% }.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x: -100% }.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50% }.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x: 100% }.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y: -100% }.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48% }.data-\[state\=closed\]\:duration-300[data-state=closed]{animation-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{animation-duration:.5s}.data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction=vertical]:after{content:var(--tw-content);left:0}.data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction=vertical]:after{content:var(--tw-content);height:.25rem}.data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction=vertical]:after{content:var(--tw-content);width:100%}.data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction=vertical]:after{content:var(--tw-content);--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction=vertical]:after{content:var(--tw-content);--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-900:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.dark\:text-gray-100:is(.dark *){--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}@media(max-width:2100px){.max-\[2100px\]\:w-\[calc\(100\%-200px\)\]{width:calc(100% - 200px)}}@media(max-width:1700px){.max-\[1700px\]\:w-\[calc\(100\%-40px\)\]{width:calc(100% - 40px)}}@media(max-width:1440px){.max-\[1440px\]\:w-\[calc\(100\%-160px\)\]{width:calc(100% - 160px)}}@media(max-width:900px){.max-\[900px\]\:w-\[calc\(100\%-40px\)\]{width:calc(100% - 40px)}}@media not all and (min-width:801px){.max-mobile\:block{display:block}.max-mobile\:hidden{display:none}.max-mobile\:w-full{width:100%}.max-mobile\:justify-between{justify-content:space-between}}@media(max-width:800px){.max-\[800px\]\:w-full{width:100%}.max-\[800px\]\:max-w-full{max-width:100%}}@media(min-width:640px){.sm\:max-w-sm{max-width:24rem}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}@media(min-width:801px){.min-\[801px\]\:hidden{display:none}}.\[\&\:first-child\]\:rounded-t-\[3px\]:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.\[\&\:first-child\]\:border-\[0px\]:first-child{border-width:0px}.\[\&\:last-child\]\:border-b:last-child{border-bottom-width:1px}.\[\&\>\*\]\:w-full>*{width:100%}.\[\&\>button\[type\=button\]\]\:hidden>button[type=button]{display:none}.\[\&\>button\]\:hidden>button{display:none}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction=vertical]>div{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&_\*\]\:cursor-default *{cursor:default}.\[\&_\.cm-gutters\]\:border-0 .cm-gutters{border-width:0px}.\[\&_\.cm-gutters\]\:bg-white .cm-gutters{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.\[\&_\.cm-gutters\]\:pe-\[1\.2em\] .cm-gutters{padding-inline-end:1.2em}.\[\&_\.cm-gutters\]\:ps-\[0\.5em\] .cm-gutters{padding-inline-start:.5em}.\[\&_\.cm-gutters\]\:text-\[\#bbb\] .cm-gutters{--tw-text-opacity: 1;color:rgb(187 187 187 / var(--tw-text-opacity, 1))}.\[\&_\.cm-panels\]\:sticky .cm-panels{position:sticky}.\[\&_\.cm-panels\]\:h-\[39px\] .cm-panels{height:39px}.\[\&_\.cm-panels\]\:-translate-y-\[100\%\] .cm-panels{--tw-translate-y: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&_\.cm-panels\]\:translate-y-0 .cm-panels{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&_\.cm-panels\]\:border-none .cm-panels{border-style:none}.\[\&_\.cm-panels\]\:bg-white .cm-panels{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.\[\&_\.cm-panels\]\:pl-\[20px\] .cm-panels{padding-left:20px}.\[\&_\.cm-scroller\>div\:nth-child\(2\)\]\:flex-1 .cm-scroller>div:nth-child(2){flex:1 1 0%}.\[\&_\.cm-scroller\>div\:nth-child\(2\)\]\:\[white-space\:break-spaces\] .cm-scroller>div:nth-child(2){white-space:break-spaces}.\[\&_\.cm-search\]\:bg-white .cm-search{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.\[\&_\.cm-search_\*\]\:hidden .cm-search *{display:none}.\[\&_\.cm-search_input\:first-child\]\:block .cm-search input:first-child{display:block}.\[\&_\.cm-search_input\:first-child\]\:w-\[300px\] .cm-search input:first-child{width:300px}.\[\&_\.cm-search_input\:first-child\]\:rounded-\[5px\] .cm-search input:first-child{border-radius:5px}.\[\&_\.copy-icon\]\:\!block .copy-icon{display:block!important}.\[\&_\.title\]\:max-w-\[90\%\] .title{max-width:90%}.\[\&_img\]\:opacity-60 img{opacity:.6}.\[\&_span\]\:block span{display:block}.\[\&_span\]\:overflow-hidden span{overflow:hidden}.\[\&_span\]\:text-ellipsis span{text-overflow:ellipsis}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\]\:\[stroke\:\#006E53\] svg{stroke:#006e53} ================================================ FILE: src/parlant/api/chat/dist/assets/manifest-BRNJYplA.webmanifest ================================================ { "name": "Parlant", "short_name": "Parlant", "description": "Chatbot by Parlant", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#006e54", "icons": [ { "src": "/chat/logo-color.svg", "sizes": "any", "type": "image/svg+xml" } ] } ================================================ FILE: src/parlant/api/chat/dist/fonts/Inter/inter.css ================================================ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 100; src: url('/fonts/Inter/static/Inter_28pt-Thin.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 200; src: url('/fonts/Inter/static/Inter_28pt-Thin.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 300; src: url('/fonts/Inter/static/Inter_28pt-Light.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; src: url('/fonts/Inter/static/Inter_28pt-Regular.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; src: url('/fonts/Inter/static/Inter_28pt-Medium.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; src: url('/fonts/Inter/static/Inter_28pt-SemiBold.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; src: url('/fonts/Inter/static/Inter_28pt-Bold.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; src: url('/fonts/Inter/static/Inter_28pt-ExtraBold.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 900; src: url('/fonts/Inter/static/Inter_28pt-ExtraBold.ttf') format('truetype'); } ================================================ FILE: src/parlant/api/chat/dist/fonts/ibm-plex-mono/ibm-plex-mono.css ================================================ @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 100; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Thin.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 200; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Thin.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 300; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Light.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Medium.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 600; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-SemiBold.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 700; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 800; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 900; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf') format('truetype'); } ================================================ FILE: src/parlant/api/chat/dist/fonts/ibm-plex-mono/static/OFL.txt ================================================ Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: https://openfontlicense.org ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: src/parlant/api/chat/dist/fonts/ubuntu-mono/static/UFL.txt ================================================ ------------------------------- UBUNTU FONT LICENCE Version 1.0 ------------------------------- PREAMBLE This licence allows the licensed fonts to be used, studied, modified and redistributed freely. The fonts, including any derivative works, can be bundled, embedded, and redistributed provided the terms of this licence are met. The fonts and derivatives, however, cannot be released under any other licence. The requirement for fonts to remain under this licence does not require any document created using the fonts or their derivatives to be published under this licence, as long as the primary purpose of the document is not to be a vehicle for the distribution of the fonts. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this licence and clearly marked as such. This may include source files, build scripts and documentation. "Original Version" refers to the collection of Font Software components as received under this licence. "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Copyright Holder(s)" refers to all individuals and companies who have a copyright ownership of the Font Software. "Substantially Changed" refers to Modified Versions which can be easily identified as dissimilar to the Font Software by users of the Font Software comparing the Original Version with the Modified Version. To "Propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification and with or without charging a redistribution fee), making available to the public, and in some countries other activities as well. PERMISSION & CONDITIONS This licence does not grant any rights under trademark law and all such rights are reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to propagate the Font Software, subject to the below conditions: 1) Each copy of the Font Software must contain the above copyright notice and this licence. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine- readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 2) The font name complies with the following: (a) The Original Version must retain its name, unmodified. (b) Modified Versions which are Substantially Changed must be renamed to avoid use of the name of the Original Version or similar names entirely. (c) Modified Versions which are not Substantially Changed must be renamed to both (i) retain the name of the Original Version and (ii) add additional naming elements to distinguish the Modified Version from the Original Version. The name of such Modified Versions must be the name of the Original Version, with "derivative X" where X represents the name of the new work, appended to that name. 3) The name(s) of the Copyright Holder(s) and any contributor to the Font Software shall not be used to promote, endorse or advertise any Modified Version, except (i) as required by this licence, (ii) to acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with their explicit written permission. 4) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this licence, and must not be distributed under any other licence. The requirement for fonts to remain under this licence does not affect any document created using the Font Software, except any version of the Font Software extracted from a document created using the Font Software may only be distributed under this licence. TERMINATION This licence becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: src/parlant/api/chat/dist/fonts/ubuntu-mono/ubuntu_mono.css ================================================ @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 100; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 200; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 300; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 400; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 500; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 600; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 700; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 800; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 900; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } ================================================ FILE: src/parlant/api/chat/dist/fonts/ubuntu-sans/README.txt ================================================ Ubuntu Sans Variable Font ========================= This download contains Ubuntu Sans as both variable fonts and static fonts. Ubuntu Sans is a variable font with these axes: wdth wght This means all the styles are contained in these files: Ubuntu_Sans/UbuntuSans-VariableFont_wdth,wght.ttf Ubuntu_Sans/UbuntuSans-Italic-VariableFont_wdth,wght.ttf If your app fully supports variable fonts, you can now pick intermediate styles that aren’t available as static fonts. Not all apps support variable fonts, and in those cases you can use the static font files for Ubuntu Sans: Ubuntu_Sans/static/UbuntuSans_Condensed-Thin.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraLight.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Light.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Regular.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Medium.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-SemiBold.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Bold.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraBold.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Thin.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraLight.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Light.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Regular.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Medium.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-SemiBold.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Bold.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraBold.ttf Ubuntu_Sans/static/UbuntuSans-Thin.ttf Ubuntu_Sans/static/UbuntuSans-ExtraLight.ttf Ubuntu_Sans/static/UbuntuSans-Light.ttf Ubuntu_Sans/static/UbuntuSans-Regular.ttf Ubuntu_Sans/static/UbuntuSans-Medium.ttf Ubuntu_Sans/static/UbuntuSans-SemiBold.ttf Ubuntu_Sans/static/UbuntuSans-Bold.ttf Ubuntu_Sans/static/UbuntuSans-ExtraBold.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ThinItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraLightItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-LightItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Italic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-MediumItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-SemiBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-BoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ThinItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraLightItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-LightItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Italic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-MediumItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-SemiBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-BoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans-ThinItalic.ttf Ubuntu_Sans/static/UbuntuSans-ExtraLightItalic.ttf Ubuntu_Sans/static/UbuntuSans-LightItalic.ttf Ubuntu_Sans/static/UbuntuSans-Italic.ttf Ubuntu_Sans/static/UbuntuSans-MediumItalic.ttf Ubuntu_Sans/static/UbuntuSans-SemiBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans-BoldItalic.ttf Ubuntu_Sans/static/UbuntuSans-ExtraBoldItalic.ttf Get started ----------- 1. Install the font files you want to use 2. Use your app's font picker to view the font family and all the available styles Learn more about variable fonts ------------------------------- https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts https://variablefonts.typenetwork.com https://medium.com/variable-fonts In desktop apps https://theblog.adobe.com/can-variable-fonts-illustrator-cc https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts Online https://developers.google.com/fonts/docs/getting_started https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts Installing fonts MacOS: https://support.apple.com/en-us/HT201749 Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows Android Apps https://developers.google.com/fonts/docs/android https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts License ------- Please read the full license text (UFL.txt) to understand the permissions, restrictions and requirements for usage, redistribution, and modification. You can use them in your products & projects – print or digital, commercial or otherwise. This isn't legal advice, please consider consulting a lawyer and see the full license for all details. ================================================ FILE: src/parlant/api/chat/dist/fonts/ubuntu-sans/UFL.txt ================================================ ------------------------------- UBUNTU FONT LICENCE Version 1.0 ------------------------------- PREAMBLE This licence allows the licensed fonts to be used, studied, modified and redistributed freely. The fonts, including any derivative works, can be bundled, embedded, and redistributed provided the terms of this licence are met. The fonts and derivatives, however, cannot be released under any other licence. The requirement for fonts to remain under this licence does not require any document created using the fonts or their derivatives to be published under this licence, as long as the primary purpose of the document is not to be a vehicle for the distribution of the fonts. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this licence and clearly marked as such. This may include source files, build scripts and documentation. "Original Version" refers to the collection of Font Software components as received under this licence. "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Copyright Holder(s)" refers to all individuals and companies who have a copyright ownership of the Font Software. "Substantially Changed" refers to Modified Versions which can be easily identified as dissimilar to the Font Software by users of the Font Software comparing the Original Version with the Modified Version. To "Propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification and with or without charging a redistribution fee), making available to the public, and in some countries other activities as well. PERMISSION & CONDITIONS This licence does not grant any rights under trademark law and all such rights are reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to propagate the Font Software, subject to the below conditions: 1) Each copy of the Font Software must contain the above copyright notice and this licence. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine- readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 2) The font name complies with the following: (a) The Original Version must retain its name, unmodified. (b) Modified Versions which are Substantially Changed must be renamed to avoid use of the name of the Original Version or similar names entirely. (c) Modified Versions which are not Substantially Changed must be renamed to both (i) retain the name of the Original Version and (ii) add additional naming elements to distinguish the Modified Version from the Original Version. The name of such Modified Versions must be the name of the Original Version, with "derivative X" where X represents the name of the new work, appended to that name. 3) The name(s) of the Copyright Holder(s) and any contributor to the Font Software shall not be used to promote, endorse or advertise any Modified Version, except (i) as required by this licence, (ii) to acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with their explicit written permission. 4) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this licence, and must not be distributed under any other licence. The requirement for fonts to remain under this licence does not affect any document created using the Font Software, except any version of the Font Software extracted from a document created using the Font Software may only be distributed under this licence. TERMINATION This licence becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: src/parlant/api/chat/dist/fonts/ubuntu-sans/ubuntu_sans.css ================================================ @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 100; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Thin.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 200; src: url('/fonts/ubuntu-sans/static/UbuntuSans-ExtraLight.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 300; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Light.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 400; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Regular.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 500; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Medium.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 600; src: url('/fonts/ubuntu-sans/static/UbuntuSans-SemiBold.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 700; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Bold.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 800; src: url('/fonts/ubuntu-sans/static/UbuntuSans-ExtraBold.ttf') format('truetype') } ================================================ FILE: src/parlant/api/chat/dist/index.html ================================================ Parlant
================================================ FILE: src/parlant/api/chat/eslint.config.js ================================================ import js from '@eslint/js'; import globals from 'globals'; import reactHooks from 'eslint-plugin-react-hooks'; import reactRefresh from 'eslint-plugin-react-refresh'; import tseslint from 'typescript-eslint'; export default tseslint.config( {ignores: ['dist']}, { extends: [js.configs.recommended, ...tseslint.configs.recommended], files: ['**/*.{ts,tsx}'], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, quotes: ['warn', 'single'], semi: ['warn', 'always'], 'react-refresh/only-export-components': ['warn', {allowConstantExport: true}], }, } ); ================================================ FILE: src/parlant/api/chat/index.html ================================================ Parlant
================================================ FILE: src/parlant/api/chat/manifest.webmanifest ================================================ { "name": "Parlant", "short_name": "Parlant", "description": "Chatbot by Parlant", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#006e54", "icons": [ { "src": "/chat/logo-color.svg", "sizes": "any", "type": "image/svg+xml" } ] } ================================================ FILE: src/parlant/api/chat/package.json ================================================ { "name": "chat", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", "test": "vitest", "test-ui": "vitest --ui" }, "dependencies": { "@codemirror/basic-setup": "^0.20.0", "@codemirror/lang-javascript": "^6.2.3", "@codemirror/language": "^6.10.8", "@codemirror/search": "^6.5.10", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.36.4", "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-radio-group": "^1.2.2", "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.2.2", "@radix-ui/react-tooltip": "^1.1.3", "@testing-library/user-event": "^14.5.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cross-spawn": "^7.0.6", "jotai": "^2.11.0", "lucide-react": "^0.453.0", "next-themes": "^0.3.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-helmet": "^6.1.0", "react-markdown": "^9.0.1", "react-resizable-panels": "^2.1.7", "rehype-highlight": "^7.0.1", "rehype-raw": "^7.0.0", "remark-breaks": "^4.0.0", "remark-gfm": "^4.0.0", "sonner": "^1.5.0", "tailwind-merge": "^2.5.4", "tailwindcss-animate": "^1.0.7", "vaul": "^1.1.2", "vite": "^7.1.12" }, "devDependencies": { "@eslint/js": "^9.11.1", "@testing-library/jest-dom": "^6.6.2", "@testing-library/react": "^16.0.1", "@types/jest": "^29.5.13", "@types/node": "^22.7.5", "@types/react": "^18.3.10", "@types/react-dom": "^18.3.0", "@types/react-helmet": "^6.1.11", "@vitejs/plugin-react": "^4.3.2", "@vitest/ui": "^4.0.6", "autoprefixer": "^10.4.20", "eslint": "^9.11.1", "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.12", "globals": "^15.9.0", "jsdom": "^25.0.1", "postcss": "^8.4.47", "sass-embedded": "^1.85.1", "tailwindcss": "^3.4.14", "typescript": "^5.5.3", "typescript-eslint": "^8.7.0", "vitest": "^4.0.6" } } ================================================ FILE: src/parlant/api/chat/postcss.config.js ================================================ export default { plugins: { tailwindcss: {}, autoprefixer: {}, }, } ================================================ FILE: src/parlant/api/chat/public/fonts/Inter/inter.css ================================================ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 100; src: url('/fonts/Inter/static/Inter_28pt-Thin.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 200; src: url('/fonts/Inter/static/Inter_28pt-Thin.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 300; src: url('/fonts/Inter/static/Inter_28pt-Light.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; src: url('/fonts/Inter/static/Inter_28pt-Regular.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; src: url('/fonts/Inter/static/Inter_28pt-Medium.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; src: url('/fonts/Inter/static/Inter_28pt-SemiBold.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; src: url('/fonts/Inter/static/Inter_28pt-Bold.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; src: url('/fonts/Inter/static/Inter_28pt-ExtraBold.ttf') format('truetype'); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 900; src: url('/fonts/Inter/static/Inter_28pt-ExtraBold.ttf') format('truetype'); } ================================================ FILE: src/parlant/api/chat/public/fonts/ibm-plex-mono/ibm-plex-mono.css ================================================ @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 100; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Thin.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 200; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Thin.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 300; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Light.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Medium.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 600; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-SemiBold.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 700; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 800; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 900; src: url('/fonts/ibm-plex-mono/static/IBMPlexMono-Bold.ttf') format('truetype'); } ================================================ FILE: src/parlant/api/chat/public/fonts/ibm-plex-mono/static/OFL.txt ================================================ Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: https://openfontlicense.org ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: src/parlant/api/chat/public/fonts/ubuntu-mono/static/UFL.txt ================================================ ------------------------------- UBUNTU FONT LICENCE Version 1.0 ------------------------------- PREAMBLE This licence allows the licensed fonts to be used, studied, modified and redistributed freely. The fonts, including any derivative works, can be bundled, embedded, and redistributed provided the terms of this licence are met. The fonts and derivatives, however, cannot be released under any other licence. The requirement for fonts to remain under this licence does not require any document created using the fonts or their derivatives to be published under this licence, as long as the primary purpose of the document is not to be a vehicle for the distribution of the fonts. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this licence and clearly marked as such. This may include source files, build scripts and documentation. "Original Version" refers to the collection of Font Software components as received under this licence. "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Copyright Holder(s)" refers to all individuals and companies who have a copyright ownership of the Font Software. "Substantially Changed" refers to Modified Versions which can be easily identified as dissimilar to the Font Software by users of the Font Software comparing the Original Version with the Modified Version. To "Propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification and with or without charging a redistribution fee), making available to the public, and in some countries other activities as well. PERMISSION & CONDITIONS This licence does not grant any rights under trademark law and all such rights are reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to propagate the Font Software, subject to the below conditions: 1) Each copy of the Font Software must contain the above copyright notice and this licence. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine- readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 2) The font name complies with the following: (a) The Original Version must retain its name, unmodified. (b) Modified Versions which are Substantially Changed must be renamed to avoid use of the name of the Original Version or similar names entirely. (c) Modified Versions which are not Substantially Changed must be renamed to both (i) retain the name of the Original Version and (ii) add additional naming elements to distinguish the Modified Version from the Original Version. The name of such Modified Versions must be the name of the Original Version, with "derivative X" where X represents the name of the new work, appended to that name. 3) The name(s) of the Copyright Holder(s) and any contributor to the Font Software shall not be used to promote, endorse or advertise any Modified Version, except (i) as required by this licence, (ii) to acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with their explicit written permission. 4) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this licence, and must not be distributed under any other licence. The requirement for fonts to remain under this licence does not affect any document created using the Font Software, except any version of the Font Software extracted from a document created using the Font Software may only be distributed under this licence. TERMINATION This licence becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: src/parlant/api/chat/public/fonts/ubuntu-mono/ubuntu_mono.css ================================================ @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 100; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 200; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 300; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 400; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Regular.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 500; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 600; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 700; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 800; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } @font-face { font-family: 'Ubuntu Mono'; font-style: normal; font-weight: 900; src: url('/fonts/ubuntu-mono/static/UbuntuMono-Bold.ttf') format('truetype'); } ================================================ FILE: src/parlant/api/chat/public/fonts/ubuntu-sans/README.txt ================================================ Ubuntu Sans Variable Font ========================= This download contains Ubuntu Sans as both variable fonts and static fonts. Ubuntu Sans is a variable font with these axes: wdth wght This means all the styles are contained in these files: Ubuntu_Sans/UbuntuSans-VariableFont_wdth,wght.ttf Ubuntu_Sans/UbuntuSans-Italic-VariableFont_wdth,wght.ttf If your app fully supports variable fonts, you can now pick intermediate styles that aren’t available as static fonts. Not all apps support variable fonts, and in those cases you can use the static font files for Ubuntu Sans: Ubuntu_Sans/static/UbuntuSans_Condensed-Thin.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraLight.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Light.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Regular.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Medium.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-SemiBold.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Bold.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraBold.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Thin.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraLight.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Light.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Regular.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Medium.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-SemiBold.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Bold.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraBold.ttf Ubuntu_Sans/static/UbuntuSans-Thin.ttf Ubuntu_Sans/static/UbuntuSans-ExtraLight.ttf Ubuntu_Sans/static/UbuntuSans-Light.ttf Ubuntu_Sans/static/UbuntuSans-Regular.ttf Ubuntu_Sans/static/UbuntuSans-Medium.ttf Ubuntu_Sans/static/UbuntuSans-SemiBold.ttf Ubuntu_Sans/static/UbuntuSans-Bold.ttf Ubuntu_Sans/static/UbuntuSans-ExtraBold.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ThinItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraLightItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-LightItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-Italic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-MediumItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-SemiBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-BoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_Condensed-ExtraBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ThinItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraLightItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-LightItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-Italic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-MediumItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-SemiBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-BoldItalic.ttf Ubuntu_Sans/static/UbuntuSans_SemiCondensed-ExtraBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans-ThinItalic.ttf Ubuntu_Sans/static/UbuntuSans-ExtraLightItalic.ttf Ubuntu_Sans/static/UbuntuSans-LightItalic.ttf Ubuntu_Sans/static/UbuntuSans-Italic.ttf Ubuntu_Sans/static/UbuntuSans-MediumItalic.ttf Ubuntu_Sans/static/UbuntuSans-SemiBoldItalic.ttf Ubuntu_Sans/static/UbuntuSans-BoldItalic.ttf Ubuntu_Sans/static/UbuntuSans-ExtraBoldItalic.ttf Get started ----------- 1. Install the font files you want to use 2. Use your app's font picker to view the font family and all the available styles Learn more about variable fonts ------------------------------- https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts https://variablefonts.typenetwork.com https://medium.com/variable-fonts In desktop apps https://theblog.adobe.com/can-variable-fonts-illustrator-cc https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts Online https://developers.google.com/fonts/docs/getting_started https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts Installing fonts MacOS: https://support.apple.com/en-us/HT201749 Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows Android Apps https://developers.google.com/fonts/docs/android https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts License ------- Please read the full license text (UFL.txt) to understand the permissions, restrictions and requirements for usage, redistribution, and modification. You can use them in your products & projects – print or digital, commercial or otherwise. This isn't legal advice, please consider consulting a lawyer and see the full license for all details. ================================================ FILE: src/parlant/api/chat/public/fonts/ubuntu-sans/UFL.txt ================================================ ------------------------------- UBUNTU FONT LICENCE Version 1.0 ------------------------------- PREAMBLE This licence allows the licensed fonts to be used, studied, modified and redistributed freely. The fonts, including any derivative works, can be bundled, embedded, and redistributed provided the terms of this licence are met. The fonts and derivatives, however, cannot be released under any other licence. The requirement for fonts to remain under this licence does not require any document created using the fonts or their derivatives to be published under this licence, as long as the primary purpose of the document is not to be a vehicle for the distribution of the fonts. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this licence and clearly marked as such. This may include source files, build scripts and documentation. "Original Version" refers to the collection of Font Software components as received under this licence. "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Copyright Holder(s)" refers to all individuals and companies who have a copyright ownership of the Font Software. "Substantially Changed" refers to Modified Versions which can be easily identified as dissimilar to the Font Software by users of the Font Software comparing the Original Version with the Modified Version. To "Propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification and with or without charging a redistribution fee), making available to the public, and in some countries other activities as well. PERMISSION & CONDITIONS This licence does not grant any rights under trademark law and all such rights are reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to propagate the Font Software, subject to the below conditions: 1) Each copy of the Font Software must contain the above copyright notice and this licence. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine- readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 2) The font name complies with the following: (a) The Original Version must retain its name, unmodified. (b) Modified Versions which are Substantially Changed must be renamed to avoid use of the name of the Original Version or similar names entirely. (c) Modified Versions which are not Substantially Changed must be renamed to both (i) retain the name of the Original Version and (ii) add additional naming elements to distinguish the Modified Version from the Original Version. The name of such Modified Versions must be the name of the Original Version, with "derivative X" where X represents the name of the new work, appended to that name. 3) The name(s) of the Copyright Holder(s) and any contributor to the Font Software shall not be used to promote, endorse or advertise any Modified Version, except (i) as required by this licence, (ii) to acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with their explicit written permission. 4) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this licence, and must not be distributed under any other licence. The requirement for fonts to remain under this licence does not affect any document created using the Font Software, except any version of the Font Software extracted from a document created using the Font Software may only be distributed under this licence. TERMINATION This licence becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ================================================ FILE: src/parlant/api/chat/public/fonts/ubuntu-sans/ubuntu_sans.css ================================================ @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 100; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Thin.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 200; src: url('/fonts/ubuntu-sans/static/UbuntuSans-ExtraLight.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 300; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Light.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 400; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Regular.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 500; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Medium.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 600; src: url('/fonts/ubuntu-sans/static/UbuntuSans-SemiBold.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 700; src: url('/fonts/ubuntu-sans/static/UbuntuSans-Bold.ttf') format('truetype') } @font-face { font-family: 'Ubuntu Sans'; font-style: normal; font-weight: 800; src: url('/fonts/ubuntu-sans/static/UbuntuSans-ExtraBold.ttf') format('truetype') } ================================================ FILE: src/parlant/api/chat/setupTests.ts ================================================ import '@testing-library/jest-dom/vitest'; ================================================ FILE: src/parlant/api/chat/src/App.css ================================================ #root { height: 100vh; margin: auto; font-family: 'Inter'; } body { pointer-events: all !important; } .fixed-scroll { overflow: scroll; scrollbar-width: thin; scrollbar-color: #ebecf0 transparent; } .fixed-scroll:hover { scrollbar-color: #cdcdcd transparent; } .fixed-scroll::-webkit-scrollbar { width: 10px; } .fixed-scroll::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.5); border-radius: 10px; } .fixed-scroll::-webkit-scrollbar-track { background: transparent; } .markdown * { font-size: revert; font-weight: revert; padding: revert; margin: revert; list-style-type: revert; color: revert; text-decoration: revert; } img { user-select: none; } .bubblesWrapper { height: fit-content; width: fit-content; background-color: #f5f9f7; padding: 10px; margin: 10px; margin-inline-start: 20px; border-radius: 15px; } .bubbles { height: 15px; width: 31px; aspect-ratio: 2.5; --_g: no-repeat radial-gradient(farthest-side, #333333 90%, #0000); background: var(--_g), var(--_g), var(--_g); background-size: 25% 50%; animation: l43 1s infinite linear; } @keyframes l43 { 0% { background-position: calc(0 * 100% / 2) 50%, calc(1 * 100% / 2) 50%, calc(2 * 100% / 2) 50%; } 20% { background-position: calc(0 * 100% / 2) 0, calc(1 * 100% / 2) 50%, calc(2 * 100% / 2) 50%; } 40% { background-position: calc(0 * 100% / 2) 100%, calc(1 * 100% / 2) 0, calc(2 * 100% / 2) 50%; } 60% { background-position: calc(0 * 100% / 2) 50%, calc(1 * 100% / 2) 100%, calc(2 * 100% / 2) 0; } 80% { background-position: calc(0 * 100% / 2) 50%, calc(1 * 100% / 2) 50%, calc(2 * 100% / 2) 100%; } 100% { background-position: calc(0 * 100% / 2) 50%, calc(1 * 100% / 2) 50%, calc(2 * 100% / 2) 50%; } } @keyframes animate-slide-down { from { max-height: 0; } to { max-height: 300px; min-height: fit-content; } } @keyframes animate-slide-up { from { max-height: 150px; } to { max-height: 0; } } .animate-slide-down { animation: animate-slide-down 0.5s ease-out forwards; } .animate-slide-up { animation: animate-slide-up 0.3s linear forwards; } ================================================ FILE: src/parlant/api/chat/src/App.tsx ================================================ import './App.css'; import Chatbot from './components/chatbot/chatbot'; import {useWebSocket} from './hooks/useWebSocket'; import {BASE_URL} from './utils/api'; import {handleChatLogs} from './utils/logs'; const WebSocketComp = () => { const socket = useWebSocket(`${BASE_URL}/logs`, true, null, handleChatLogs); void socket; return
; }; function App() { return (
); } export default App; ================================================ FILE: src/parlant/api/chat/src/components/agents-list/agent-list.module.scss ================================================ .select { padding: 0 !important; button { display: none; } } ================================================ FILE: src/parlant/api/chat/src/components/agents-list/agent-list.tsx ================================================ import {AgentInterface, CustomerInterface, SessionInterface} from '@/utils/interfaces'; import {ReactNode, useEffect} from 'react'; import {spaceClick} from '@/utils/methods'; import {DialogDescription, DialogHeader, DialogTitle} from '../ui/dialog'; import clsx from 'clsx'; import {useAtom} from 'jotai'; import {agentAtom, agentsAtom, customerAtom, customersAtom, dialogAtom, newSessionAtom, sessionAtom} from '@/store'; import Avatar from '../avatar/avatar'; export const NEW_SESSION_ID = 'NEW_SESSION'; const newSessionObj: SessionInterface = { customer_id: '', title: 'New Conversation', agent_id: '', creation_utc: new Date().toLocaleString('en-US'), id: NEW_SESSION_ID, }; const AgentList = (): ReactNode => { const [, setSession] = useAtom(sessionAtom); const [agent, setAgent] = useAtom(agentAtom); const [agents] = useAtom(agentsAtom); const [customers] = useAtom(customersAtom); const [, setCustomer] = useAtom(customerAtom); const [, setNewSession] = useAtom(newSessionAtom); const [dialog] = useAtom(dialogAtom); useEffect(() => { if (agents?.length && agents.length === 1) selectAgent(agents[0]); }, []); const selectAgent = (agent: AgentInterface): void => { setAgent(agent); if (customers.length < 2) { selectCustomer(customers?.[0], agent); } }; const selectCustomer = (customer: CustomerInterface, currAgent?: AgentInterface) => { setAgent(agent || currAgent || null); setCustomer(customer); setNewSession({...newSessionObj, agent_id: agent?.id as string, customer_id: customer.id}); setSession(newSessionObj); dialog.closeDialog(); }; return (
{agent ? 'Select a Customer' : 'Select an Agent'} close
{(agent ? customers : agents)?.map((entity) => (
(agent ? selectCustomer(entity) : selectAgent(entity))} key={entity.id} className={clsx('cursor-pointer hover:bg-[#FBFBFB] min-h-[78px] h-[78px] w-full border-b-[0.6px] border-b-solid border-b-[#EBECF0] flex items-center ps-[30px] pe-[20px]')}>
{entity.id === 'guest' ? 'Guest' : entity.name}
(id={entity.id})
))}
); }; export default AgentList; ================================================ FILE: src/parlant/api/chat/src/components/avatar/avatar.tsx ================================================ /* eslint-disable react-refresh/only-export-components */ import {AgentInterface, CustomerInterface} from '@/utils/interfaces'; import React, {ReactNode} from 'react'; import Tooltip from '../ui/custom/tooltip'; import {twMerge} from 'tailwind-merge'; interface Props { agent: AgentInterface; customer?: CustomerInterface; tooltip?: boolean; } interface Color { text: string; background: string; outerBackground: string; iconBackground?: string; iconText?: string; } const colors = { green: {dark: 'rgb(80 130 1)', light: 'rgb(80 130 1 / 10%)', extraLight: 'rgb(80 130 1 / 5%)'}, purple: {dark: 'rgb(85 1 104)', light: 'rgb(85 1 104 / 10%)', extraLight: 'rgb(85 1 104 / 5%)'}, pink: {dark: 'rgb(155 3 95)', light: 'rgb(155 3 95 / 10%)', extraLight: 'rgb(155 3 95 / 5%)'}, orange: {dark: 'rgb(183 99 0)', light: 'rgb(183 99 0 / 10%)', extraLight: 'rgb(183 99 0 / 5%)'}, blue: {dark: 'rgb(46 128 108)', light: 'rgb(46 128 108 / 10%)', extraLight: 'rgb(46 128 108 / 5%)'}, }; const agentColors: Color[] = [ {text: 'white', background: colors.green.dark, outerBackground: colors.green.light}, {text: 'white', background: colors.purple.dark, outerBackground: colors.purple.light}, {text: 'white', background: colors.pink.dark, outerBackground: colors.pink.light}, {text: 'white', background: colors.orange.dark, outerBackground: colors.orange.light}, {text: 'white', background: colors.blue.dark, outerBackground: colors.blue.light}, ]; const customerColors: Color[] = [ {iconBackground: colors.green.dark, background: colors.green.light, text: colors.green.dark, outerBackground: colors.green.extraLight}, {iconBackground: colors.purple.dark, background: colors.purple.light, text: colors.purple.dark, outerBackground: colors.purple.extraLight}, {iconBackground: colors.pink.dark, background: colors.pink.light, text: colors.pink.dark, outerBackground: colors.pink.extraLight}, {iconBackground: colors.orange.dark, background: colors.orange.light, text: colors.orange.dark, outerBackground: colors.orange.extraLight}, {iconBackground: colors.blue.dark, background: colors.blue.light, text: colors.blue.dark, outerBackground: colors.blue.extraLight}, ]; export const getAvatarColor = (id: string, type: 'agent' | 'customer') => { const palette = type === 'agent' ? agentColors : customerColors; const hash = [...id].reduce((acc, char) => acc + char.charCodeAt(0), 0); return palette[hash % palette.length]; }; const Avatar = ({agent, customer, tooltip = true}: Props): ReactNode => { const agentColor = getAvatarColor(agent.id, 'agent'); const customerColor = customer && getAvatarColor(customer.id, 'customer'); const isAgentUnavailable = agent?.name === 'N/A'; const isCustomerUnavailable = customer?.name === 'N/A'; const agentFirstLetter = agent.name.replaceAll(/>|
{isAgentUnavailable ? 'N/A' : agentFirstLetter}
{agent && customer && (
{isCustomerUnavailable ? 'N/A' : customerFirstLetter}
)}
); }; export default Avatar; ================================================ FILE: src/parlant/api/chat/src/components/canned-response/canned-response.tsx ================================================ import Tooltip from '../ui/custom/tooltip'; import {copy} from '@/lib/utils'; import {twMerge} from 'tailwind-merge'; // const TooltipComponent = ({fragmentId}: {fragmentId: string}) => { // return ( //
copy(fragmentId)}> //
Fragment ID: {fragmentId}
// //
// ); // }; const CannedResponse = ({cannedResponse: cannedResponse}: {cannedResponse: {id: string; value: string}}) => { const [id, value] = cannedResponse?.value || ['', '']; return (
{value || 'loading'}
copy(id || '', e.currentTarget)} className='hidden absolute right-[10px] top-[8px] cursor-pointer size-[28px] group-hover:flex justify-center items-center bg-white hover:bg-[#F3F5F9] border border-[#EEEEEE] hover:border-[#E9EBEF] rounded-[6px]'>
// } side='top' align='start' className='ml-[23px] -mb-[10px] font-medium font-inter'> // ); }; export default CannedResponse; ================================================ FILE: src/parlant/api/chat/src/components/canned-responses/canned-responses.tsx ================================================ import {useState} from 'react'; import {ClassNameValue, twMerge} from 'tailwind-merge'; import CannedResponse from '../canned-response/canned-response'; import ErrorBoundary from '../error-boundary/error-boundary'; export interface Utterance { id: string; value: string; } const CannedResponses = ({cannedResponses: cannedResponses, className}: {cannedResponses: {id: string; value: string}[]; className?: ClassNameValue}) => { const [isOpen, setIsOpen] = useState(false); const onToggle = (e: any) => { setIsOpen(e.target.open); }; return (
Canned Responses
Could not load canned responses
}> {cannedResponses.map((cannedResponse) => ( ))}
); }; export default CannedResponses; ================================================ FILE: src/parlant/api/chat/src/components/chat-header/chat-header.tsx ================================================ import {ReactNode, useEffect, useState} from 'react'; import Tooltip from '../ui/custom/tooltip'; import {spaceClick} from '@/utils/methods'; import AgentList from '../agents-list/agent-list'; import {Menu} from 'lucide-react'; import {Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger} from '../ui/sheet'; import SessionList from '../session-list/session-list'; import HeaderWrapper from '../header-wrapper/header-wrapper'; import {useAtom} from 'jotai'; import {agentAtom, dialogAtom, sessionAtom} from '@/store'; import {Input} from '../ui/input'; // import DarkModeToggle from '../dark-mode-toggle/dark-mode-toggle'; export const NEW_SESSION_ID = 'NEW_SESSION'; const ChatHeader = ({setFilterSessionVal, filterSessionVal}: {setFilterSessionVal: (value: string) => void; filterSessionVal: string}): ReactNode => { const [sheetOpen, setSheetOpen] = useState(false); const [session, setSession] = useAtom(sessionAtom); const [, setAgent] = useAtom(agentAtom); const [dialog] = useAtom(dialogAtom); useEffect(() => { if (sheetOpen) setSheetOpen(false); // eslint-disable-next-line react-hooks/exhaustive-deps }, [session]); const createNewSession = () => { setSession(null); setAgent(null); dialog.openDialog('', , {height: '536px', width: '604px'}); }; return (
logo
setSheetOpen(!sheetOpen)}> setSheetOpen(true)}>
setFilterSessionVal(e.target.value)} className='!ring-0 !ring-offset-0 h-[38px] w-full placeholder:font-light ps-[35px] rounded-[6px] !pointer-events-auto' />
logo
setFilterSessionVal(e.target.value)} className='!ring-0 !ring-offset-0 h-[38px] w-full placeholder:font-light ps-[35px] rounded-[6px] !pointer-events-auto' />
<> add session add session
{/*
*/}
); }; export default ChatHeader; ================================================ FILE: src/parlant/api/chat/src/components/chatbot/chatbot.tsx ================================================ /* eslint-disable react-refresh/only-export-components */ import {createContext, ReactElement, useEffect, useState} from 'react'; import SessionList from '../session-list/session-list'; import ErrorBoundary from '../error-boundary/error-boundary'; import ChatHeader from '../chat-header/chat-header'; import {useDialog} from '@/hooks/useDialog'; import {Helmet} from 'react-helmet'; import AgentList, {NEW_SESSION_ID} from '../agents-list/agent-list'; import {useAtom} from 'jotai'; import {agentAtom, dialogAtom, sessionAtom, sessionsAtom} from '@/store'; import {twMerge} from 'tailwind-merge'; import SessionView from '../session-view/session-view'; import {spaceClick} from '@/utils/methods'; export const SessionProvider = createContext({}); const SessionsSection = () => { const [filterSessionVal, setFilterSessionVal] = useState(''); return (
); }; export default function Chatbot(): ReactElement { // const SessionView = lazy(() => import('../session-view/session-view')); const [sessionName, setSessionName] = useState(''); const {openDialog, DialogComponent, closeDialog} = useDialog(); const [showMessage, setShowMessage] = useState(false); const [sessions] = useAtom(sessionsAtom); const [session, setSession] = useAtom(sessionAtom); const [, setDialog] = useAtom(dialogAtom); const [filterSessionVal, setFilterSessionVal] = useState(''); const [, setAgent] = useAtom(agentAtom); const [dialog] = useAtom(dialogAtom); useEffect(() => { if (sessions) { setShowMessage(!!sessions.length); } setTimeout(() => { setShowMessage(true); }, 500); }, [sessions]); useEffect(() => { if (session?.id) { if (session?.id === NEW_SESSION_ID) setSessionName('Parlant | New Session'); else { const sessionTitle = session?.title; if (sessionTitle) setSessionName(`Parlant | ${sessionTitle}`); } } else setSessionName('Parlant'); // eslint-disable-next-line react-hooks/exhaustive-deps }, [session?.id]); useEffect(() => { setDialog({openDialog, closeDialog}); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const createNewSession = () => { setSession(null); setAgent(null); dialog.openDialog('', , {height: '536px', width: '604px'}); }; return (
{session?.id ? (
) : (
{showMessage && !sessions.length ? 'Start a session to begin chatting' : 'Select or start a session to begin chatting'}
add session add session
)}
); } ================================================ FILE: src/parlant/api/chat/src/components/dark-mode-toggle/dark-mode-toggle.tsx ================================================ import { ReactNode, useEffect, useState } from 'react'; const DarkModeToggle = (): ReactNode =>{ const getInitialTheme = () => localStorage.getItem('theme') || 'light'; const [theme, setTheme] = useState(getInitialTheme); useEffect(() => { const root = window.document.documentElement; if (theme === 'dark') { root.classList.add('dark'); } else { root.classList.remove('dark'); } localStorage.setItem('theme', theme); }, [theme]); const toggleTheme = () => { setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light')); }; return (
); }; export default DarkModeToggle; ================================================ FILE: src/parlant/api/chat/src/components/error-boundary/error-boundary.tsx ================================================ import {Component, ReactNode} from 'react'; interface Props { children: ReactNode; component?: ReactNode; } interface State { hasError: boolean; errorStack?: string; } export default class ErrorBoundary extends Component { constructor(props: Props) { super(props); this.state = {hasError: false}; } static getDerivedStateFromError() { return {hasError: true}; } componentDidCatch(error: Error) { this.setState({errorStack: error.stack}); } render(): ReactNode { return this.state.hasError ? this.props.component || (
Logo

Oops! Something went wrong

We apologize for the inconvenience. Please try again later, or{' '} try again now .

{this.state.errorStack}
) : this.props.children; } } ================================================ FILE: src/parlant/api/chat/src/components/gradient-button/gradient-button.module.scss ================================================ .colorsButton { .children { color: white; border-radius: 6px; border: none; } &:hover, &:focus { background: linear-gradient(89.94deg, #FFB800, #B4E64A, #87DAC6, #FF68C3, #005CE7); background-size: 200% 200%; div { background: linear-gradient(89.94deg, #FFB800, #B4E64A, #87DAC6, #FF68C3, #005CE7); } .children { color: black !important; background-color: #ffffffc8 !important; } } } ================================================ FILE: src/parlant/api/chat/src/components/gradient-button/gradient-button.tsx ================================================ import { spaceClick } from '@/utils/methods'; import styles from './gradient-button.module.scss'; import { ReactElement, ReactNode } from 'react'; interface GradientButtonProps { className?: string; buttonClassName?: string; children: ReactNode onClick: (e: React.MouseEvent) => void; } export default function GradientButton({className, buttonClassName, children, onClick}: GradientButtonProps): ReactElement { return (
{children}
); } ================================================ FILE: src/parlant/api/chat/src/components/header-wrapper/header-wrapper.tsx ================================================ import {ReactNode} from 'react'; import {twMerge} from 'tailwind-merge'; const HeaderWrapper = ({children, className}: {children?: ReactNode; className?: string}) => { return
{children}
; }; export default HeaderWrapper; ================================================ FILE: src/parlant/api/chat/src/components/log-filters/log-filters.tsx ================================================ import {memo, ReactNode, useEffect, useRef, useState} from 'react'; import {Button} from '../ui/button'; import {Checkbox} from '../ui/checkbox'; import {Input} from '../ui/input'; import {Dialog, DialogClose, DialogContent, DialogDescription, DialogPortal, DialogTitle, DialogTrigger} from '../ui/dialog'; import {ClassNameValue, twMerge} from 'tailwind-merge'; import {X} from 'lucide-react'; import {getDistanceToRight} from '@/utils/methods'; import Tooltip from '../ui/custom/tooltip'; import {Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue} from '../ui/select'; export type Type = 'GuidelineMatcher' | 'MessageEventComposer' | 'ToolCaller'; export type Level = 'CRITICAL' | 'ERROR' | 'WARNING' | 'INFO' | 'DEBUG' | 'TRACE'; const ALL_TYPES: Type[] = ['GuidelineMatcher', 'ToolCaller', 'MessageEventComposer']; const ALL_LEVELS: Level[] = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE']; const typeOptions: {[key in Type]: {label: string; icon: string; color: string}} = { GuidelineMatcher: { label: 'Guideline Matcher', icon: 'icons/filters/guideline-matcher-color.svg', color: '#419480', }, MessageEventComposer: { label: 'Message Event Composer', icon: 'icons/filters/message-composer-color.svg', color: '#7E3A89', }, ToolCaller: { label: 'Tool Caller', icon: 'icons/filters/tool-caller-color.svg', color: '#CB7714', }, }; const AddFilterChip = ({className}: {className?: ClassNameValue}) => { return (
{/*

+

*/}

Add Content Filter

); }; const FilterDialogContent = ({contentChanged, defaultValue}: {contentChanged: (text: string) => void; defaultValue?: string}) => { const [inputVal, setInputVal] = useState(defaultValue || ''); const onApplyClick = () => { const trimmed = inputVal.trim(); if (trimmed) contentChanged(inputVal); }; return (

Filter by content

setInputVal(e.target.value)} name='filter' className='h-[36px] !ring-0 !ring-offset-0 border-none text-[16px] bg-[#FBFBFB] hover:bg-[#F5F6F8] focus:!bg-white' />
Cancel Apply
); }; const FilterDialog = ({contentChanged, content, children, className}: {contentChanged: (text: string) => void; content?: string; children?: ReactNode; className?: ClassNameValue}) => { return ( {children || } Filter by content Filter by content ); }; const LogFilters = ({ applyFn, def, filterId, className, showDropdown, showTags, deleteFilterTab, }: { applyFn: (types: string[], level: string, content: string[]) => void; filterId?: number; def?: {level?: Level; types?: Type[]; content?: string[]} | null; className?: ClassNameValue; showDropdown?: boolean; showTags?: boolean; deleteFilterTab?: (filterId: number | undefined) => void; }) => { const [sources, setSources] = useState(structuredClone(def?.types || [])); const [contentConditions, setContentConditions] = useState(structuredClone(def?.content || [])); const [level, setLevel] = useState(def?.level || ALL_LEVELS[ALL_LEVELS.length - 1]); const [prevTabId, setPrevTabId] = useState(); useEffect(() => { if (!showTags) return; if (filterId && filterId !== prevTabId) { const types = structuredClone(def?.types || ALL_TYPES); const level = def?.level || ALL_LEVELS[ALL_LEVELS.length - 1]; const content = def?.content || []; setSources(types); setLevel(level); setContentConditions(content); applyFn(types, level, content); setPrevTabId(filterId); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [filterId]); useEffect(() => { setSources(def?.types || []); setLevel(def?.level || ALL_LEVELS[ALL_LEVELS.length - 1]); setContentConditions(def?.content || []); }, [def]); // const changeSource = (type: Type, value: boolean, cb?: (sources: Type[], level: Level, contentConditions: string[]) => void) => { // setSources((val) => { // if (value) val.push(type); // else val = val.filter((item) => item !== type); // const vals = [...new Set(val)]; // cb?.(vals, level, contentConditions); // return vals; // }); // }; const TypeChip = ({type, className}: {type: Type; className?: ClassNameValue}) => { return (
{type}

{typeOptions[type].label}

{/* changeSource(type, false, applyFn)} /> */}
); }; const CondChip = ({ text, index, apply, deleted, wrapperClassName, className, deleteButtonClassName, }: { text: string; index: number; apply?: boolean; deleted?: () => void; className?: ClassNameValue; wrapperClassName?: ClassNameValue; deleteButtonClassName?: ClassNameValue; }) => { return (

{text}

{deleted && ( { e.stopPropagation(); const newContentConditions = contentConditions?.filter((_, i) => i !== index); if (apply) { setContentConditions(newContentConditions); applyFn(sources, level, newContentConditions); } deleted?.(); }} /> )}
); }; const DropDownFilter = () => { const [dropdownOpen, setDropdownOpen] = useState(false); const [sources, setSources] = useState(structuredClone(def?.types || [])); const [content, setContent] = useState(structuredClone(def?.content || [])); const [level, setLevel] = useState(def?.level || ALL_LEVELS[ALL_LEVELS.length - 1]); const wrapperRef = useRef(null); const [usePopupToLeft, setUsePopupToLeft] = useState(false); const changeSource = (type: Type, value: boolean) => { setSources((val) => { if (value) val.push(type); else val = val.filter((item) => item !== type); const vals = [...new Set(val)]; return vals; }); }; useEffect(() => { if (!dropdownOpen) { setSources(structuredClone(def?.types || [])); setContent(structuredClone(def?.content || [])); } }, [dropdownOpen]); useEffect(() => { if (wrapperRef?.current) { if (getDistanceToRight(wrapperRef.current) < 218) setUsePopupToLeft(true); else setUsePopupToLeft(false); } }, [wrapperRef?.current?.scrollWidth, dropdownOpen]); const changeMenuOpen = () => { setDropdownOpen(!dropdownOpen); setSources(structuredClone(def?.types || [])); setContent(structuredClone(def?.content || [])); }; return (

Edit Filters

{/* */}

Filter

close

Level:


{ALL_TYPES.map((type) => (
changeSource(type, !!isChecked)} />
))}

{content?.map((item, i) => ( { setContent((c) => { c[i] = inputVal; return [...c]; }); }}> setContent(content.filter((_, index) => index !== i))} wrapperClassName='w-full !border-0 bg-[#F5F6F8] hover:bg-[#EBECF0]' className='justify-between !border-0 bg-[#F5F6F8] group-hover:bg-[#EBECF0]' deleteButtonClassName='visible' /> ))}
{!!content?.length &&
}
setContent((val) => [...val, inputVal])} />

); }; return (
{showTags && !!def?.types?.length && def.types.map((type) => )} {showTags && def?.content?.map((c: string, index: number) => )} {showDropdown && }
{deleteFilterTab && ( )}
); }; export default memo(LogFilters); ================================================ FILE: src/parlant/api/chat/src/components/markdown/markdown.tsx ================================================ /* eslint-disable @typescript-eslint/no-unused-vars */ import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import rehypeHighlight from 'rehype-highlight'; import rehypeRaw from 'rehype-raw'; import 'highlight.js/styles/github.css'; import styles from '../message/message.module.scss'; import {twMerge} from 'tailwind-merge'; function preserveBlankLines(md: string): string { return md?.replace?.(/\\n/g, '\n')?.replace(/\n(?!-)/g, '
') || md; } const Markdown = ({children, className}: {children: string; className?: string}) => { return ( }} rehypePlugins={[rehypeHighlight, rehypeRaw]} remarkPlugins={[remarkGfm]} className={twMerge('leading-[19px]', styles.markdown, className)}> {preserveBlankLines(children)} ); }; export default Markdown; ================================================ FILE: src/parlant/api/chat/src/components/message/draft-bubble.tsx ================================================ import Markdown from '../markdown/markdown'; import {twMerge} from 'tailwind-merge'; import Tooltip from '../ui/custom/tooltip'; import {copy} from '@/lib/utils'; import {useEffect, useState} from 'react'; const DraftBubble = ({draft = '', open = false}) => { const [wasOpen, setWasOpen] = useState(false); useEffect(() => { if (open) setWasOpen(true); }, [open]); return (
{draft}
copy(draft || '')} className='group cursor-pointer'> edit
); }; export default DraftBubble; ================================================ FILE: src/parlant/api/chat/src/components/message/message-bubble.tsx ================================================ /* eslint-disable react-hooks/exhaustive-deps */ import {useEffect, useRef, useState} from 'react'; import {twJoin, twMerge} from 'tailwind-merge'; import Markdown from '../markdown/markdown'; import Tooltip from '../ui/custom/tooltip'; import {Button} from '../ui/button'; import {useAtom} from 'jotai'; import {agentAtom, customerAtom, dialogAtom, sessionAtom} from '@/store'; import {getAvatarColor} from '../avatar/avatar'; // import MessageRelativeTime from './message-relative-time'; import {copy} from '@/lib/utils'; import {Eye, EyeOff, Flag, Search} from 'lucide-react'; import FlagMessage from '../message-details/flag-message'; import {EventInterface} from '@/utils/interfaces'; import DraftBubble from './draft-bubble'; interface Props { event: EventInterface; isContinual: boolean; isSameSourceAsPrevious?: boolean; isRegenerateHidden?: boolean; isFirstMessageInDate?: boolean; flagged?: string; flaggedChanged?: (flagged: string) => void; showLogsForMessage?: EventInterface | null; regenerateMessageFn?: (sessionId: string) => void; resendMessageFn?: (sessionId: string, text?: string) => void; showLogs: (event: EventInterface) => void; setIsEditing?: React.Dispatch>; sameTraceMessages?: EventInterface[]; } const MessageBubble = ({event, isFirstMessageInDate, showLogs, isContinual, showLogsForMessage, setIsEditing, flagged, flaggedChanged, sameTraceMessages: sameTraceMessages}: Props) => { const ref = useRef(null); const [agent] = useAtom(agentAtom); const [customer] = useAtom(customerAtom); const markdownRef = useRef(null); const [showDraft, setShowDraft] = useState(false); const [, setRowCount] = useState(1); const [dialog] = useAtom(dialogAtom); const [session] = useAtom(sessionAtom); // Buffered reveal system: gradually reveal text at a controlled rate const messageText = event?.data?.message || ''; const chunks = event?.data?.chunks; const isAlreadyComplete = chunks !== undefined && chunks.length > 0 && chunks[chunks.length - 1] === null; const [revealedLength, setRevealedLength] = useState(isAlreadyComplete ? messageText.length : 0); const [animatedLength, setAnimatedLength] = useState(isAlreadyComplete ? messageText.length : 0); // tracks what's been animated (for fade effect) const revealedLengthRef = useRef(isAlreadyComplete ? messageText.length : 0); const animationTimerRef = useRef | null>(null); useEffect(() => { if (!markdownRef?.current) return; const rowCount = Math.floor(markdownRef.current.offsetHeight / 24); setRowCount(rowCount + 1); }, [markdownRef, showDraft]); // Gradually reveal text at a smooth rate useEffect(() => { // Reset if message is shorter (new message) if (messageText.length < revealedLengthRef.current) { revealedLengthRef.current = 0; setRevealedLength(0); return; } // If we're already caught up, nothing to do if (revealedLengthRef.current >= messageText.length) return; // Reveal characters gradually const revealInterval = 30; // ms between reveals const charsPerReveal = 4; // characters to reveal each tick const timer = setInterval(() => { const currentRevealed = revealedLengthRef.current; const targetLength = messageText.length; if (currentRevealed >= targetLength) { clearInterval(timer); return; } // Reveal next chunk of characters const newLength = Math.min(currentRevealed + charsPerReveal, targetLength); revealedLengthRef.current = newLength; setRevealedLength(newLength); }, revealInterval); return () => clearInterval(timer); }, [messageText]); // Update animated length to catch up with revealed length (for fade effect) const revealedLengthForAnimation = useRef(0); revealedLengthForAnimation.current = revealedLength; useEffect(() => { // Reset if revealed length is less (new message) if (revealedLength < animatedLength) { if (animationTimerRef.current) { clearTimeout(animationTimerRef.current); animationTimerRef.current = null; } setAnimatedLength(revealedLength); return; } // Only start a new timer if there's no active timer and we have text to animate if (revealedLength > animatedLength && !animationTimerRef.current) { animationTimerRef.current = setTimeout(() => { animationTimerRef.current = null; // Use current revealedLength so all visible text becomes stable setAnimatedLength(revealedLengthForAnimation.current); }, 400); // Match animation duration } }, [revealedLength, animatedLength]); // Cleanup timer on unmount useEffect(() => { return () => { if (animationTimerRef.current) { clearTimeout(animationTimerRef.current); } }; }, []); // FIXME: // rowCount SHOULD in fact be automatically calculated to // benefit from nice, smaller one-line message boxes. // However, currently we couldn't make it work in all // of the following use cases in draft/canned-response switches: // 1. When both draft and canned response are multi-line // 2. When both draft and canned response are one-liners // 3. When one is a one-liner and the other isn't // Therefore for now I'm disabling isOneLiner // until fixed. -- Yam const isOneLiner = false; // FIXME: see above const isCustomer = event.source === 'customer' || event.source === 'customer_ui'; const serverStatus = event.serverStatus; const isGuest = customer?.id === 'guest'; const customerName = isGuest ? 'G' : customer?.name?.[0]?.toUpperCase(); const isViewingCurrentMessage = showLogsForMessage && showLogsForMessage.id === event.id; const colorPallete = getAvatarColor((isCustomer ? customer?.id : agent?.id) || '', isCustomer ? 'customer' : 'agent'); const name = isCustomer ? customer?.name : agent?.name; const formattedName = isCustomer && isGuest ? 'Guest' : name; const isEditDisabled = sameTraceMessages?.some((msg) => msg.serverStatus && msg.serverStatus !== 'ready' && msg.serverStatus !== 'error'); // Check if streaming is in progress (chunks property exists and not yet terminated with null) // chunks === undefined means block mode, chunks === [] means streaming started but no chunks yet, // chunks with null as last element means streaming is complete const isStreaming = chunks !== undefined && (chunks.length === 0 || chunks[chunks.length - 1] !== null); // Check if we're still revealing text (either streaming, reveal hasn't caught up, or animation hasn't caught up) const isRevealing = isStreaming || (chunks !== undefined && (revealedLength < messageText.length || animatedLength < revealedLength)); // Track previous isRevealing state to detect when streaming completes const wasRevealingRef = useRef(false); // Auto-scroll during streaming to keep new text visible useEffect(() => { const scrollContainer = ref.current?.closest('.messages'); if (!scrollContainer) return; const { scrollTop, scrollHeight, clientHeight } = scrollContainer; const isNearBottom = scrollHeight - scrollTop - clientHeight < 150; if (isRevealing && ref.current && isNearBottom) { // During streaming: scroll to keep message visible ref.current.scrollIntoView({ behavior: 'smooth', block: 'end' }); } else if (wasRevealingRef.current && !isRevealing && isNearBottom) { // Streaming just completed: scroll to very bottom of container scrollContainer.scrollTo({ top: scrollContainer.scrollHeight, behavior: 'smooth' }); } wasRevealingRef.current = isRevealing; }, [revealedLength, isRevealing]); return ( <>
{(!isContinual || isFirstMessageInDate) && (
{(isCustomer ? customerName?.[0] : agent?.name?.[0])?.toUpperCase()}
{formattedName}
{!isCustomer && sameTraceMessages?.some((e: EventInterface) => e.data?.draft) && (
)} {flagged && (
)} {/* */} {!isCustomer && ( )}
)} e.data?.draft)?.data?.draft || ''} />
{isRevealing ? ( <> {/* During reveal: split into stable text and newly revealed text with fade */} {messageText.slice(0, animatedLength)} {revealedLength > animatedLength && ( {messageText.slice(animatedLength, revealedLength)} )} ) : ( {messageText} )} {/* Blinking cursor for streaming messages */} {isStreaming && ( )}
copy(event?.data?.message || '')} className='group cursor-pointer'> edit
{isCustomer && !isEditDisabled && (
setIsEditing?.(true)} className='group cursor-pointer'> edit
)}
); }; export default MessageBubble; ================================================ FILE: src/parlant/api/chat/src/components/message/message-relative-time.tsx ================================================ /* eslint-disable react-hooks/exhaustive-deps */ import {timeAgo} from '@/lib/utils'; import {EventInterface} from '@/utils/interfaces'; import {useEffect, useRef, useState} from 'react'; const MessageRelativeTime = ({event}: {event: EventInterface}) => { const [time, setTime] = useState(event.serverStatus === 'pending' ? 'Just Now' : timeAgo(event.creation_utc)); const intervalRef = useRef(null); const setMessageRelativeTime = () => { if (intervalRef.current) clearInterval(intervalRef.current); const updateTime = () => setTime(timeAgo(event.creation_utc)); setTime(event.serverStatus === 'pending' ? 'Just Now' : timeAgo(event.creation_utc)); const creationDate = new Date(event.creation_utc); const now = new Date(); const diffMinutes = Math.floor((now.getTime() - creationDate.getTime()) / (1000 * 60)); if (diffMinutes < 60) { intervalRef.current = setInterval(updateTime, 60000); } // else if (diffMinutes < 1440) { // const minutesPastHour = creationDate.getMinutes(); // const nextFullHour = new Date(now); // nextFullHour.setMinutes(60 - minutesPastHour, 0, 0); // const timeUntilNextHour = nextFullHour.getTime() - now.getTime(); // intervalRef.current = setTimeout(() => { // updateTime(); // intervalRef.current = setInterval(updateTime, 3600000); // }, timeUntilNextHour); // } else { // const nextMidnight = new Date(now); // nextMidnight.setHours(24, 0, 0, 0); // const timeUntilMidnight = nextMidnight.getTime() - now.getTime(); // intervalRef.current = setTimeout(updateTime, timeUntilMidnight); // } return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }; useEffect(() => { if (event.serverStatus !== 'pending' && time === 'Just Now') setTime(timeAgo(event.creation_utc)); }, [event?.serverStatus]); useEffect(setMessageRelativeTime, [event.creation_utc]); return
{time}
; }; export default MessageRelativeTime; ================================================ FILE: src/parlant/api/chat/src/components/message/message.module.scss ================================================ .pendingVideo { clip-path: inset(1px 0.9px 0.5px 0.8px round 50%); } .markdown { code { white-space: break-spaces; max-width: 100%; word-break: break-word; background: transparent !important; font-size: 14px; } p { word-break: break-word; } ul { all: revert; margin: 0; padding: 0; list-style: inside; } h2 { font-weight: bold; } table { white-space: nowrap; display: block; overflow: scroll; scrollbar-width: auto; border-radius: 2px; th, td { padding-inline: 10px; text-align: start; } th { padding: 10px; } tr:last-child td { padding-bottom: 10px; } thead { border: 1px solid lightgray; border-bottom: none; border-radius: 3px 3px 0 0; padding: 10px; } tbody { border: 1px solid lightgray; border-top: none; border-radius: 0 0 3px 3px; padding: 10px; } } li > div { display: inline; } } ================================================ FILE: src/parlant/api/chat/src/components/message/message.tsx ================================================ /* eslint-disable react-hooks/exhaustive-deps */ import {ReactElement, useEffect, useRef, useState} from 'react'; import {EventInterface} from '@/utils/interfaces'; import Spacer from '../ui/custom/spacer'; import {twMerge} from 'tailwind-merge'; import {Textarea} from '../ui/textarea'; import {Button} from '../ui/button'; import {useAtom} from 'jotai'; import {sessionAtom} from '@/store'; import MessageBubble from './message-bubble'; interface Props { event: EventInterface; sameTraceMessages?: EventInterface[]; isContinual: boolean; isRegenerateHidden?: boolean; isFirstMessageInDate?: boolean; flagged?: string; flaggedChanged?: (flagged: string) => void; showLogsForMessage?: EventInterface | null; regenerateMessageFn?: (sessionId: string) => void; resendMessageFn?: (sessionId: string, text?: string) => void; showLogs: (event: EventInterface) => void; setIsEditing?: React.Dispatch>; } const MessageEditing = ({event, resendMessageFn, setIsEditing}: Props) => { const ref = useRef(null); const textArea = useRef(null); const [textValue, setTextValue] = useState(event?.data?.message || ''); const [session] = useAtom(sessionAtom); useEffect(() => { textArea?.current?.select(); }, [textArea?.current]); useEffect(() => { ref?.current?.scrollIntoView({behavior: 'smooth', block: 'nearest'}); }, [ref?.current]); return (