commit c889a57b6bb56e72f5c698e073b44e69eae94724 Author: wehub-resource-sync Date: Mon Jul 13 13:02:24 2026 +0800 chore: import upstream snapshot with attribution diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..ecb3966 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,114 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json +# .coderabbit.yaml +language: en +early_access: false +enable_free_tier: true +reviews: + profile: chill + instructions: >- + # Code Review Instructions + + - Ensure the code follows best practices and coding standards. + - For **Python** code, follow + [PEP 20](https://www.python.org/dev/peps/pep-0020/) and + [CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161) + standards. + + # Documentation Review Instructions + - Verify that documentation and comments are clear and comprehensive. + - Verify that documentation and comments are free of spelling mistakes. + + # Test Code Review Instructions + - Ensure that test code is automated, comprehensive, and follows testing best practices. + - Verify that all critical functionality is covered by tests. + - Ensure that test code follow + [CEP-8](https://gist.github.com/reactive-firewall/d840ee9990e65f302ce2a8d78ebe73f6) + + # Misc. + - Confirm that the code meets the project's requirements and objectives. + - Confirm that copyright years are up-to date whenever a file is changed. + request_changes_workflow: false + high_level_summary: true + high_level_summary_placeholder: '@coderabbitai summary' + auto_title_placeholder: '@coderabbitai' + review_status: true + poem: false + collapse_walkthrough: false + sequence_diagrams: false + changed_files_summary: true + path_filters: ['!*.xc*/**', '!node_modules/**', '!dist/**', '!build/**', '!.git/**', '!venv/**', '!__pycache__/**'] + path_instructions: + - path: README.md + instructions: >- + 1. Consider the file 'README.md' the overview/introduction of the project. + Also consider the 'README.md' file the first place to look for project documentation. + + 2. When reviewing the file 'README.md' it should be linted with help + from the tools `markdownlint` and `languagetool`, pointing out any issues. + + 3. You may assume the file 'README.md' will contain GitHub flavor Markdown. + - path: '**/*.py' + instructions: >- + When reviewing Python code for this project: + + 1. Prioritize portability over clarity, especially when dealing with cross-Python compatibility. However, with the priority in mind, do still consider improvements to clarity when relevant. + + 2. As a general guideline, consider the code style advocated in the PEP 8 standard (excluding the use of spaces for indentation) and evaluate suggested changes for code style compliance. + + 3. As a style convention, consider the code style advocated in [CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161) and evaluate suggested changes for code style compliance. + + 4. As a general guideline, try to provide any relevant, official, and supporting documentation links to any tool's suggestions in review comments. This guideline is important for posterity. + + 5. As a general rule, undocumented function definitions and class definitions in the project's Python code are assumed incomplete. Please consider suggesting a short summary of the code for any of these incomplete definitions as docstrings when reviewing. + - path: cognee/tests/* + instructions: >- + When reviewing test code: + + 1. Prioritize portability over clarity, especially when dealing with cross-Python compatibility. However, with the priority in mind, do still consider improvements to clarity when relevant. + + 2. As a general guideline, consider the code style advocated in the PEP 8 standard (excluding the use of spaces for indentation) and evaluate suggested changes for code style compliance. + + 3. As a style convention, consider the code style advocated in [CEP-8](https://gist.github.com/reactive-firewall/b7ee98df9e636a51806e62ef9c4ab161) and evaluate suggested changes for code style compliance, pointing out any violations discovered. + + 4. As a general guideline, try to provide any relevant, official, and supporting documentation links to any tool's suggestions in review comments. This guideline is important for posterity. + + 5. As a project rule, Python source files with names prefixed by the string "test_" and located in the project's "tests" directory are the project's unit-testing code. It is safe, albeit a heuristic, to assume these are considered part of the project's minimal acceptance testing unless a justifying exception to this assumption is documented. + + 6. As a project rule, any files without extensions and with names prefixed by either the string "check_" or the string "test_", and located in the project's "tests" directory, are the project's non-unit test code. "Non-unit test" in this context refers to any type of testing other than unit testing, such as (but not limited to) functional testing, style linting, regression testing, etc. It can also be assumed that non-unit testing code is usually written as Bash shell scripts. + - path: requirements.txt + instructions: >- + * The project's own Python dependencies are recorded in 'requirements.txt' for production code. + + * The project's testing-specific Python dependencies are recorded in 'tests/requirements.txt' and are used for testing the project. + + * The project's documentation-specific Python dependencies are recorded in 'docs/requirements.txt' and are used only for generating Python-focused documentation for the project. 'docs/requirements.txt' may be absent if not applicable. + + Consider these 'requirements.txt' files the records of truth regarding project dependencies. + - path: .github/** + instructions: >- + * When the project is hosted on GitHub: All GitHub-specific configurations, templates, and tools should be found in the '.github' directory tree. + + * 'actionlint' erroneously generates false positives when dealing with GitHub's `${{ ... }}` syntax in conditionals. + + * 'actionlint' erroneously generates incorrect solutions when suggesting the removal of valid `${{ ... }}` syntax. + abort_on_close: true + auto_review: + enabled: true + auto_incremental_review: true + ignore_title_keywords: [] + labels: [] + drafts: false + base_branches: + - dev + - main + tools: + shellcheck: + enabled: true + ruff: + enabled: true + markdownlint: + enabled: true + yamllint: + enabled: true +chat: + auto_reply: true diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..7f6d6f0 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +{ + "name": "Cognee Development", + "image": "ghcr.io/astral-sh/uv:python3.12-bookworm", + "features": { + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/docker-in-docker:2": {} + }, + "postCreateCommand": "uv sync && cp cognee/.env.template cognee/.env && echo 'Set LLM_API_KEY in cognee/.env to get started'", + "forwardPorts": [8000, 3000], + "portsAttributes": { + "8000": {"label": "Cognee API", "onAutoForward": "notify"}, + "3000": {"label": "Cognee UI", "onAutoForward": "notify"} + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "charliermarsh.ruff" + ], + "settings": { + "python.defaultInterpreterPath": ".venv/bin/python" + } + } + }, + "remoteEnv": { + "PYTHONPATH": "/workspaces/cognee" + } +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5579537 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +bin +dist +docs +evals +tests +tools +assets +notebooks +cognee-frontend + +examples/python/.dlt +.venv +.github + +.idea +.vscode diff --git a/.dockerignore.ci b/.dockerignore.ci new file mode 100644 index 0000000..428797c --- /dev/null +++ b/.dockerignore.ci @@ -0,0 +1,4 @@ +* +!pyproject.toml +!uv.lock +!README.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1c502dd --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# Deprecated: use .env.template as the canonical environment template. +# +# To configure Cognee, copy .env.template to .env and fill in the values you need: +# +# cp .env.template .env +# +# This file is kept for backwards compatibility with older setup instructions. + +LLM_API_KEY="your_api_key" +LLM_MODEL="openai/gpt-5-mini" +LLM_PROVIDER="openai" diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..e29f30b --- /dev/null +++ b/.env.template @@ -0,0 +1,629 @@ +############################################################################### +# TIER 1 — QUICK START +# Set this one variable and you're done. Everything else has working defaults. +# Default databases (SQLite, LanceDB, KuzuDB) are file-based, no setup needed. +############################################################################### +LLM_API_KEY="your_api_key" + + +############################################################################### +# TIER 2 — COMMON OVERRIDES (uncomment to customize) +# Most users only need a few of these. +############################################################################### + +# -- LLM Provider & Model ---------------------------------------------------- +#LLM_MODEL="openai/gpt-5-mini" +#LLM_PROVIDER="openai" +#LLM_ENDPOINT="" + +# -- Embedding Provider ------------------------------------------------------- +#EMBEDDING_PROVIDER="openai" +#EMBEDDING_MODEL="openai/text-embedding-3-large" +#EMBEDDING_DIMENSIONS=3072 + +# -- Tokenizer (chunk sizing) ------------------------------------------------- +# The tokenizer used to count tokens for chunking is auto-selected to match the +# embedding model: openai/gemini use TikToken, mistral uses the Mistral +# tokenizer, and fastembed / openai-compatible models use the embedding model's +# own HuggingFace tokenizer. cognee warns (and falls back to TikToken) when it +# cannot match one, since a mismatched tokenizer mis-sizes chunks and skews the +# --dry-run estimate. For providers whose model id is not a HuggingFace repo +# (e.g. Ollama), set HUGGINGFACE_TOKENIZER to a tokenizer matching your model: +#HUGGINGFACE_TOKENIZER="Salesforce/SFR-Embedding-Mistral" + +# -- Database Providers (switch from file-based defaults) --------------------- +#DB_PROVIDER="postgres" +#DB_HOST=127.0.0.1 +#DB_PORT=5432 +#DB_USERNAME=cognee +#DB_PASSWORD=cognee +#DB_NAME=cognee_db + +#GRAPH_DATABASE_PROVIDER="neo4j" +#VECTOR_DB_PROVIDER="lancedb" + + +############################################################################### +# TIER 3 — ADVANCED (grouped by subsystem) +# Most users never need to change anything below this line. +############################################################################### + +################################################################################ +# LLM — Advanced Settings +# Tune these when switching providers, adjusting structured output, or +# rate-limiting LLM calls. +################################################################################ + +# Structured output framework: "instructor" (default, via litellm) or "baml" +STRUCTURED_OUTPUT_FRAMEWORK="instructor" + +# Instructor's mode determines how structured data is extracted from LLM responses. +# Each LLM has its own default (e.g. gpt-5 models use "json_schema_mode"). +#LLM_INSTRUCTOR_MODE="" + +# Cognee uses this to determine optimal chunk size (not forwarded in LLM calls). +#LLM_MAX_COMPLETION_TOKENS="16384" + +# LLM API version (needed for Azure OpenAI) +#LLM_API_VERSION="" + +# Extra kwargs passed to every LLM completion call (JSON string). +# Examples: LLM_ARGS='{"max_tokens": 16384, "temperature": 0.7}' +#LLM_ARGS='{}' + +# LLM rate limiting +#LLM_RATE_LIMIT_ENABLED=true +#LLM_RATE_LIMIT_REQUESTS=60 +#LLM_RATE_LIMIT_INTERVAL=60 + +# Per-stage model routing (optional). Unset means the stage uses the base LLM_* config above. +# Route a cheap or local model to extraction (it runs per chunk and dominates token use), +# and keep a stronger model for summarization and query-time reasoning. +#LLM_EXTRACTION_MODEL="ollama_chat/llama3.1" +#LLM_EXTRACTION_PROVIDER="ollama" +#LLM_EXTRACTION_ENDPOINT="http://localhost:11434" +#LLM_EXTRACTION_API_KEY="" +#LLM_SUMMARIZATION_MODEL="openai/gpt-5-mini" +#LLM_SUMMARIZATION_PROVIDER="openai" +#LLM_QUERY_MODEL="openai/gpt-5-mini" +#LLM_QUERY_PROVIDER="openai" + +################################################################################ +# Embedding — Advanced Settings +# Tune these when using non-default embedding providers. +################################################################################ + +#EMBEDDING_ENDPOINT="" +#EMBEDDING_API_VERSION="" +#EMBEDDING_MAX_COMPLETION_TOKENS=8191 +#EMBEDDING_BATCH_SIZE=36 +# If not provided, LLM_API_KEY is used for embeddings too. +#EMBEDDING_API_KEY="your_api_key" + +################################################################################ +# BAML Structured Output +# Only needed when STRUCTURED_OUTPUT_FRAMEWORK="baml". +################################################################################ + +#BAML_LLM_PROVIDER=openai +#BAML_LLM_MODEL="gpt-5-mini" +#BAML_LLM_ENDPOINT="" +#BAML_LLM_API_KEY="your_api_key" +#BAML_LLM_API_VERSION="" + +################################################################################ +# Root Directories +# Override where Cognee stores files and databases (default: .venv). +################################################################################ + +#DATA_ROOT_DIRECTORY='/Users//Desktop/cognee/.cognee_data/' +#SYSTEM_ROOT_DIRECTORY='/Users//Desktop/cognee/.cognee_system/' + +################################################################################ +# Storage Backend +# Switch from local filesystem to S3. +################################################################################ + +#STORAGE_BACKEND="local" +#STORAGE_BACKEND="s3" +#STORAGE_BUCKET_NAME="your-bucket-name" +#AWS_REGION="us-east-1" +#AWS_ACCESS_KEY_ID="your-access-key" +#AWS_SECRET_ACCESS_KEY="your-secret-key" +#DATA_ROOT_DIRECTORY="s3://your-bucket/cognee/data" +#SYSTEM_ROOT_DIRECTORY="s3://your-bucket/cognee/system" +#CACHE_ROOT_DIRECTORY="s3://your-bucket/cognee/cache" + +################################################################################ +# Relational Database — Advanced +# Connection tuning, pool sizes, SSL. +################################################################################ + +DB_PROVIDER="sqlite" +DB_NAME=cognee_db + +# Custom connection arguments (JSON). Useful for SSL, timeouts. +#DATABASE_CONNECT_ARGS='{"sslmode": "require", "connect_timeout": 10}' + +# Connection pool tuning (JSON). +#POOL_ARGS='{"pool_size": 5, "max_overflow": 10, "pool_recycle": -1, "pool_timeout": 30}' + +################################################################################ +# Graph Database — Advanced +# Provider-specific connection details. +################################################################################ + +GRAPH_DATABASE_PROVIDER="kuzu" +# Handler for multi-user access control (per-dataset DB creation). +GRAPH_DATASET_DATABASE_HANDLER="kuzu" + +# Remote Kuzu +#GRAPH_DATABASE_PROVIDER="kuzu-remote" +#GRAPH_DATABASE_URL="http://localhost:8000" +#GRAPH_DATABASE_USERNAME=XXX +#GRAPH_DATABASE_PASSWORD=YYY + +# Neo4j +#GRAPH_DATABASE_PROVIDER="neo4j" +#GRAPH_DATABASE_URL=bolt://localhost:7687 +#GRAPH_DATABASE_NAME="neo4j" +#GRAPH_DATABASE_USERNAME=neo4j +#GRAPH_DATABASE_PASSWORD=pleaseletmein + +################################################################################ +# Vector Database — Advanced +# Provider-specific connection details. +################################################################################ + +# Supported (built-in): pgvector | lancedb +# Community adapters (separate packages): qdrant | weaviate | milvus | chromadb +VECTOR_DB_PROVIDER="lancedb" +#VECTOR_DB_URL= +#VECTOR_DB_KEY= +# Handler for multi-user access control (per-dataset DB creation). +VECTOR_DATASET_DATABASE_HANDLER="lancedb" + +# Connection pool tuning for PGVector per-dataset engines (JSON). +# When ENABLE_BACKEND_ACCESS_CONTROL=true each dataset gets its own engine; this controls +# its pool size independently from POOL_ARGS (default: pool_size=2, max_overflow=2). +#VECTOR_POOL_ARGS='{"pool_size": 2, "max_overflow": 5, "pool_recycle": 1800}' + +################################################################################ +# Ontology Resolver +# Use when grounding extraction against an OWL ontology. +################################################################################ + +#ONTOLOGY_RESOLVER=rdflib +#MATCHING_STRATEGY=fuzzy +#ONTOLOGY_FILE_PATH=YOUR_FULL_FILE_PATH + +################################################################################ +# Database Adapter Caching +# Max graph / vector / relational engine instances held in the LRU cache +# (one per unique connection key, e.g. per dataset in multi-tenant mode). +# In subprocess mode, this also caps how many child processes (Kuzu/LanceDB +# workers) can be alive at once — eviction shuts down the subprocess. +# Also the default for DATASET_QUEUE_MAX_CONCURRENT when that is unset. +# Engines of datasets currently admitted by the dataset queue are pinned and +# never evicted by capacity pressure; when every entry is pinned the cache +# briefly exceeds this size (bounded by DATASET_QUEUE_MAX_CONCURRENT). +# Lower values save memory; raise when running many datasets concurrently. +################################################################################ + +#DATABASE_MAX_LRU_CACHE_SIZE=6 + +################################################################################ +# Dataset Queue +# Semaphore-backed queue that limits how many datasets can be processed at +# once (cognify, search, etc.). Prevents resource exhaustion when many +# datasets run in parallel. When the limit is reached, new datasets wait +# until a slot is freed. +################################################################################ + +#DATASET_QUEUE_ENABLED=true +# Max concurrent dataset slots. Defaults to DATABASE_MAX_LRU_CACHE_SIZE. +#DATASET_QUEUE_MAX_CONCURRENT=6 + +################################################################################ +# Translation +# Use when ingesting non-English content. +################################################################################ + +TRANSLATION_PROVIDER="llm" +TARGET_LANGUAGE="en" +CONFIDENCE_THRESHOLD=0.8 +#GOOGLE_TRANSLATE_API_KEY="your-google-api-key" +#GOOGLE_PROJECT_ID="your-google-project-id" +#AZURE_TRANSLATOR_KEY="your-azure-translator-key" +#AZURE_TRANSLATOR_REGION="westeurope" +#AZURE_TRANSLATOR_ENDPOINT="https://api.cognitive.microsofttranslator.com" +#TRANSLATION_BATCH_SIZE=10 +#TRANSLATION_MAX_RETRIES=3 +#TRANSLATION_TIMEOUT_SECONDS=30 + +################################################################################ +# Data Migrations (graph/vector revision chain) +################################################################################ + +# Cognee runs its data migrations automatically on startup (FastAPI lifespan, +# first remember()/cognify() call in an SDK process). Set to false to disable +# ALL automatic runs and migrate explicitly via `cognee-cli upgrade` instead +# (e.g. operator-driven deployments, or tests on deliberately old-format data). +#ENABLE_AUTO_MIGRATIONS=true + +################################################################################ +# Migration (Relational -> Graph) +################################################################################ + +MIGRATION_DB_PATH="/path/to/migration/directory" +MIGRATION_DB_NAME="migration_database.sqlite" +MIGRATION_DB_PROVIDER="sqlite" +#MIGRATION_DB_USERNAME=cognee +#MIGRATION_DB_PASSWORD=cognee +#MIGRATION_DB_HOST="127.0.0.1" +#MIGRATION_DB_PORT=5432 + +################################################################################ +# Security +################################################################################ + +# -- JWT Authentication ------------------------------------------------------- +# Secret used to sign and verify JWT tokens. Must be the same across all instances +# (e.g. all Kubernetes pods) for tokens issued by one instance to be accepted by another. +# Change this to a long random string in production. Never commit the real value to git. +FASTAPI_USERS_JWT_SECRET="super_secret" + +# How long a JWT token remains valid, in seconds. After expiry the user must log in again. +# The same lifetime applies to both cookie and bearer token auth. +# Default: 3600 (1 hour) +JWT_LIFETIME_SECONDS=3600 + +# -- API Key Authentication --------------------------------------------------- +# When HASH_API_KEY=true, API keys are hashed with SHA-256 before being stored in the database. +# This means the raw key is shown to the user only once at creation time and cannot be recovered. +# +# ⚠️ Migration note: if you enable this on a running system that already has API keys stored +# in plaintext, those existing keys will stop working immediately because the lookup will +# hash the incoming value and find no match. You must either: +# 1. Delete and re-issue all existing API keys, or +# 2. Write a one-off migration to SHA-256 hash the existing api_key column values. +# +# Default: false (keys are stored in plaintext) +HASH_API_KEY="False" + +# When set to false don't allow adding of local system files to Cognee. Should be set to False when Cognee is used as a backend. +ACCEPT_LOCAL_FILE_PATH=True +ALLOW_HTTP_REQUESTS=True +ALLOW_CYPHER_QUERY=True +RAISE_INCREMENTAL_LOADING_ERRORS=True + +# Authentication & access control. +# +# ENABLE_BACKEND_ACCESS_CONTROL is the canonical posture switch: +# true (default) - multi-tenant mode: per-user/dataset isolated DBs AND +# API endpoints require an authenticated user. +# false - single-user mode: shared DB AND auth requirement off. +# +# REQUIRE_AUTHENTICATION is an explicit override on the auth requirement only: +# unset (default) - follow ENABLE_BACKEND_ACCESS_CONTROL. +# true - force auth on (sane for single-user behind a token). +# false - force auth off — IGNORED if ENABLE_BACKEND_ACCESS_CONTROL +# is true (multi-tenant always requires auth; a warning is +# logged at startup). +# +# Startup logs an "auth posture: ..." line with the resolved decision so you +# can verify what's actually in effect. +REQUIRE_AUTHENTICATION=False + +# Set this variable to True to enforce usage of backend access control for Cognee +# Note: This is only currently supported by the following databases: +# Relational: SQLite, Postgres +# Vector: LanceDB, pgvector +# Graph: KuzuDB, neo4j_aura_dev +# +# It enforces creation of databases per Cognee user + dataset. Does not work with some graph and database providers. +# Disable mode when using not supported graph/vector databases. +ENABLE_BACKEND_ACCESS_CONTROL=True + +################################################################################ +# Cloud Sync +################################################################################ + +COGNEE_CLOUD_API_URL="http://localhost:8001" +COGNEE_CLOUD_AUTH_TOKEN="your-api-key" + +################################################################################ +# UI +################################################################################ + +UI_APP_URL=http://localhost:3000 + +################################################################################ +# DLT Ingestion +################################################################################ + +#DLT_MAX_ROWS_PER_TABLE=50 + +################################################################################ +# Dev / Debug +################################################################################ + +ENV="local" +#ENABLE_LAST_ACCESSED="false" +TOKENIZERS_PARALLELISM="false" + +# -- Search History ------------------------------------------------------------ +# Set to false to disable search query/result logging (recommended for daemons) +#COGNEE_LOG_SEARCH_HISTORY="true" + +# LITELLM Logging Level. Set to quiet down logging +LITELLM_LOG="ERROR" +#TELEMETRY_DISABLED=1 +#DEFAULT_USER_EMAIL="" +#DEFAULT_USER_PASSWORD="" + +# -- Cognee Logging ----------------------------------------------------------- +# Console log level: DEBUG, INFO, WARNING, ERROR, CRITICAL (default: INFO) +#LOG_LEVEL="INFO" +# Set to false to disable file logging entirely (console-only) +#COGNEE_LOG_FILE="true" +# Override the log directory (default: ~/.cognee/logs) +#COGNEE_LOGS_DIR="/var/log/cognee" +# Max size per log file before rotation, in bytes (default: 50 MB) +#COGNEE_LOG_MAX_BYTES=52428800 +# Number of rotated log files to keep (default: 5 → 300 MB total cap) +#COGNEE_LOG_BACKUP_COUNT=5 + +################################################################################ +# AWS +################################################################################ + +#AWS_REGION="" +#AWS_ENDPOINT_URL="" +#AWS_ACCESS_KEY_ID="" +#AWS_SECRET_ACCESS_KEY="" +#AWS_SESSION_TOKEN="" + +################################################################################ +# Web Scraper +################################################################################ + +WEB_SCRAPER_TIMEOUT=15.0 +WEB_SCRAPER_MAX_DELAY=10.0 + +################################################################################ +# OpenTelemetry / Tracing +################################################################################ + +# -- To export traces to an OTLP-compatible backend (Dash0, Grafana, Jaeger, etc.), +# set the endpoint and optional auth headers: --------------------- +# COGNEE_TRACING_ENABLED=true +# OTEL_EXPORTER_OTLP_ENDPOINT="https://ingress.eu-west.dash0.com:4317" +# OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer " + +# Override the service name reported in traces (default: "cognee") +# OTEL_SERVICE_NAME="cognee" + +# Add extra resource attributes (useful for Kubernetes, multi-instance deployments) +# OTEL_RESOURCE_ATTRIBUTES="service.namespace=my-team,service.version=1.0" + +# Session cache settings +# To switch to Redis caching check our documentation page sessions-and-caching +# CACHING=true +# Backends: sqlite (default), postgres, redis, fs, tapes +# CACHE_BACKEND=sqlite +# CACHE_BACKEND=postgres +# Optional explicit SQLAlchemy async URL for the sqlite/postgres backends. +# sqlite default: cache.db next to the relational SQLite database. +# postgres default: falls back to DB_* settings when DB_PROVIDER=postgres. +# CACHE_DB_URL=sqlite+aiosqlite:///path/to/databases/cache.db +# CACHE_DB_URL=postgresql+asyncpg://cognee:cognee@localhost:5432/cognee_db +# Minimum seconds between global TTL purge sweeps (sqlite/postgres backends) +# CACHE_PURGE_INTERVAL_SECONDS=900 + + +################################################################################ +# ADDITIONAL MANAGED SETTINGS (previously undocumented) +# These are all read by Cognee's config classes (pydantic BaseSettings) but +# were missing from this template. Defaults shown; uncomment to override. +################################################################################ + +# -- LLM tuning --------------------------------------------------------------- +#LLM_TEMPERATURE=0.0 +#LLM_STREAMING=false +# Optional fallback model used when the primary completion fails. +#FALLBACK_MODEL="" +#FALLBACK_API_KEY="" +#FALLBACK_ENDPOINT="" +# Audio transcription model. +#TRANSCRIPTION_MODEL="whisper-1" + +# -- Embedding rate limiting (mirrors the LLM_RATE_LIMIT_* knobs) ------------- +#EMBEDDING_RATE_LIMIT_ENABLED=false +#EMBEDDING_RATE_LIMIT_REQUESTS=60 +#EMBEDDING_RATE_LIMIT_INTERVAL=60 +#EMBEDDING_RATE_LIMIT_TOKENS=0 +# Token-based LLM limit (0 = disabled; requests/interval already documented above). +#LLM_RATE_LIMIT_TOKENS=0 + +# -- Chunking ----------------------------------------------------------------- +#CHUNK_SIZE=1500 +#CHUNK_OVERLAP=10 +#CHUNK_STRATEGY="paragraph" + +# -- Triplet embedding (extra triplet-level vectors during cognify) ----------- +#TRIPLET_EMBEDDING=false + +# -- Session cache (Redis backend + session/usage tuning) --------------------- +# Used when CACHE_BACKEND=redis; also the host/port for a remote cache. +#CACHE_HOST="localhost" +#CACHE_PORT=6379 +#CACHE_USERNAME="" +#CACHE_PASSWORD="" +# Session lifetime in the cache (default 7 days) and per-turn context cap. +#SESSION_TTL_SECONDS=604800 +#MAX_SESSION_CONTEXT_CHARS= +# Self-improvement: absorb per-turn feedback/guidance automatically (default on). +#AUTO_FEEDBACK=true +# Per-process LLM usage logging into the cache. +#USAGE_LOGGING=false +#USAGE_LOGGING_TTL=604800 +# Cross-process locks for file-based embedded graph backends. +#SHARED_KUZU_LOCK=false +#SHARED_LADYBUG_LOCK=false + +# -- Graph database — advanced connection / Kuzu tuning ----------------------- +#GRAPH_DATABASE_HOST="" +#GRAPH_DATABASE_PORT= +#GRAPH_DATABASE_KEY="" +#GRAPH_DATABASE_ALLOW_ANONYMOUS=false +# Run the embedded graph engine (Kuzu/Ladybug) in a worker subprocess. +#GRAPH_DATABASE_SUBPROCESS_ENABLED=true +# Kuzu performance tuning (0/auto by default). +#KUZU_NUM_THREADS=0 +#KUZU_BUFFER_POOL_SIZE= +#KUZU_MAX_DB_SIZE= + +# -- Vector database — advanced connection ------------------------------------ +#VECTOR_DB_HOST="" +#VECTOR_DB_PORT=1234 +#VECTOR_DB_NAME="" +#VECTOR_DB_USERNAME="" +#VECTOR_DB_PASSWORD="" +#VECTOR_DB_SUBPROCESS_ENABLED=true + +# -- Database subprocess workers — advanced tuning ---------------------------- +# The embedded DB engines (Kuzu/Ladybug graph, LanceDB vector) run their native +# client in a dedicated worker process. These knobs tune that harness. +# Per-RPC deadline guarding against a hung native call (seconds; <=0 disables). +#SUBPROCESS_CALL_TIMEOUT=300 +# How many times a failed subprocess RPC is retried (respawning the worker). +#SUBPROCESS_MAX_RETRIES=2 +# Backstop for the brief window where one graph worker is still releasing a +# file lock while another opens the same DB path: the worker retries the open +# this many times, with exponential backoff starting at this many seconds +# (per-attempt backoff is capped internally). +#SUBPROCESS_OPEN_LOCK_RETRIES=10 +#SUBPROCESS_OPEN_LOCK_BACKOFF=0.1 + +# -- AWS / Bedrock extras (in addition to the AWS section above) -------------- +#AWS_PROFILE_NAME="" +#AWS_BEDROCK_RUNTIME_ENDPOINT="" + +# -- Local llama.cpp provider ------------------------------------------------- +#LLAMA_CPP_MODEL_PATH="" +#LLAMA_CPP_N_CTX=2048 +#LLAMA_CPP_N_GPU_LAYERS=0 +#LLAMA_CPP_CHAT_FORMAT="chatml" + +# -- Security: additional auth-token secrets ---------------------------------- +# Like FASTAPI_USERS_JWT_SECRET above, these default to the INSECURE value +# "super_secret". Override BOTH with long random strings in production. +#FASTAPI_USERS_VERIFICATION_TOKEN_SECRET="change_me_in_production" +#FASTAPI_USERS_RESET_PASSWORD_TOKEN_SECRET="change_me_in_production" + + +################################################################################ +# Docker / MCP Runtime +# Configure the cognee API image (cognee/cognee) and the MCP image +# (cognee/cognee-mcp) when running `docker run` / `docker compose`. +# Unless noted "read by the app", these are consumed by the container +# entrypoints/compose and have defaults baked into the images — set them only +# to override. (docker-compose.yml already sets sensible values for most.) +################################################################################ + +# -- API server (cognee/cognee image) ---------------------------------------- +# CORS allow-list for the FastAPI server: comma-separated origins. Read by the +# app (cognee/api/client.py). Default '*' (all origins) — set explicit domains +# in production. +#CORS_ALLOWED_ORIGINS="https://yourdomain.com,https://another.com" +# Server bind/port inside the container (entrypoint defaults shown). +#HTTP_PORT=8000 +#BIND_ADDRESS=0.0.0.0 + +# -- MCP server (cognee/cognee-mcp image) ------------------------------------- +# Transport the MCP container serves. The Docker image reads TRANSPORT_MODE; +# the direct `cognee-mcp` CLI uses --transport instead. +#TRANSPORT_MODE=stdio # stdio | sse | http +# Comma-separated optional extras to pip-install at container startup. +#EXTRAS=aws,postgres +# MCP "API mode": point the MCP server at an already-running cognee API server. +#API_URL=http://localhost:8000 +#API_TOKEN="" +# MCP "Cloud mode": point the MCP server at a managed Cognee Cloud instance. +# These are the canonical cloud-connection variables, shared across serve(), +# push(), the MCP server, and sync. COGNEE_CLOUD_API_URL / COGNEE_CLOUD_AUTH_TOKEN +# (above) remain as deprecated fallbacks. +#COGNEE_SERVICE_URL="" +#COGNEE_API_KEY="" + +# -- Debug (both images) ------------------------------------------------------ +# DEBUG=true together with ENV in {dev,local} starts the container under +# debugpy, listening on DEBUG_PORT. ENV is the canonical environment variable +# (set it in the Dev/Debug section above); ENVIRONMENT is a deprecated alias +# still accepted by the container entrypoints. +#DEBUG=false +#DEBUG_PORT=5678 + +# -- Frontend (cognee-frontend image / compose `ui` profile) ------------------ +#NEXT_PUBLIC_BACKEND_API_URL=http://localhost:8000 + + +############################################################################### +# TIER 4 — EXAMPLE PROVIDER OVERRIDES (commented out) +# Uncomment + fill values to switch providers. +############################################################################### + +########## Azure OpenAI (API key auth) ######################################## +#LLM_PROVIDER="azure" +#LLM_MODEL="azure/gpt-5-mini" +#LLM_ENDPOINT="https://YOUR-RESOURCE.openai.azure.com" +#LLM_API_KEY="your-azure-api-key" +#LLM_API_VERSION="2024-12-01-preview" +#LLM_MAX_COMPLETION_TOKENS="16384" + +########## Azure OpenAI (managed identity / DefaultAzureCredential) ########### +# Uses DefaultAzureCredential - no API key needed (for Azure VMs, App Service, etc.) +# Requires: pip install azure-identity +#LLM_PROVIDER="azure" +#LLM_MODEL="azure/gpt-5-mini" +#LLM_ENDPOINT="https://YOUR-RESOURCE.openai.azure.com" +#LLM_API_VERSION="2024-12-01-preview" +#LLM_AZURE_USE_MANAGED_IDENTITY=true + +#EMBEDDING_MODEL="azure/text-embedding-3-large" +#EMBEDDING_ENDPOINT="https://YOUR-RESOURCE.openai.azure.com/openai/deployments/text-embedding-3-large" +#EMBEDDING_API_KEY="your-azure-api-key" +#EMBEDDING_API_VERSION="2024-12-01-preview" +#EMBEDDING_DIMENSIONS=3072 +#EMBEDDING_MAX_COMPLETION_TOKENS=8191 + +########## Local LLM via Ollama ############################################### +#LLM_API_KEY ="ollama" +#LLM_MODEL="llama3.1:8b" +#LLM_PROVIDER="ollama" +#LLM_ENDPOINT="http://localhost:11434/v1" +#EMBEDDING_PROVIDER="ollama" +#EMBEDDING_MODEL="nomic-embed-text:latest" +#EMBEDDING_ENDPOINT="http://localhost:11434/api/embed" +#EMBEDDING_DIMENSIONS=768 +#HUGGINGFACE_TOKENIZER="nomic-ai/nomic-embed-text-v1.5" + +########## OpenRouter (also free) ############################################# +#LLM_API_KEY="< /dev/null && [ -d "/app/.venv" ]; then + echo "in-container=true" >> "$GITHUB_OUTPUT" + else + echo "in-container=false" >> "$GITHUB_OUTPUT" + fi + + - name: Set up Python + if: steps.detect-env.outputs.in-container != 'true' + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Install uv + if: steps.detect-env.outputs.in-container != 'true' + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + + - name: Rebuild uv lockfile + if: ${{ inputs.rebuild-lockfile == 'true' }} + shell: bash + run: | + rm uv.lock + uv lock + + - name: Install dependencies (bare runner) + if: steps.detect-env.outputs.in-container != 'true' + shell: bash + run: | + EXTRA_ARGS="" + if [ -n "${{ inputs.extra-dependencies }}" ]; then + IFS=' ' read -r -a deps <<< "${{ inputs.extra-dependencies }}" + for extra in "${deps[@]}"; do + EXTRA_ARGS="$EXTRA_ARGS --extra $extra" + done + fi + uv sync --extra api --extra docs --extra evals --extra codegraph --extra ollama --extra dev --extra neo4j --extra redis --extra tracing $EXTRA_ARGS + + - name: Install dependencies (container - link project only) + if: steps.detect-env.outputs.in-container == 'true' + shell: bash + env: + UV_PROJECT_ENVIRONMENT: /app/.venv + run: | + # Persist for all subsequent steps so uv run uses /app/.venv + echo "UV_PROJECT_ENVIRONMENT=/app/.venv" >> "$GITHUB_ENV" + # Install project into the pre-built venv; --inexact keeps pre-installed extras intact + uv sync --frozen --inexact + # Prevent uv run from re-syncing and stripping extras + echo "UV_NO_SYNC=true" >> "$GITHUB_ENV" + + - name: Add telemetry identifier for telemetry test and in case telemetry is enabled by accident + shell: bash + run: | + echo "test-machine" > .anon_id diff --git a/.github/actions/image_builder/action.yaml b/.github/actions/image_builder/action.yaml new file mode 100644 index 0000000..974a761 --- /dev/null +++ b/.github/actions/image_builder/action.yaml @@ -0,0 +1,35 @@ +name: 'Build Docker images for Cognee' +description: 'Build cognee-related Docker images and push to the Docker registry (AWS ECR)' +inputs: + stage: + description: 'The stage of the pipeline, such as "dev" or "prd", for the Cognee app' + required: true + aws_account_id: + description: 'The AWS account ID for the Cognee app' + required: true + should_publish: + description: 'Whether to publish the Cognee Docker image to AWS ECR; should be either "true" or "false"' + required: true + ecr_image_repo_name: + description: 'The Docker image ECR repository name for the Cognee app, such as "workflows"' + required: true + dockerfile_location: + description: 'The directory location of the Dockerfile for the Cognee app' + required: true + +runs: + using: "composite" + steps: + - name: Build PromethAI App Docker image + shell: bash + env: + STAGE: ${{ inputs.stage }} + run: | + export SHA_SHORT="$(git rev-parse --short HEAD)" + export CUR_DATE="$(date +%Y%m%d%H%M%S)" + export VERSION="${{ inputs.stage }}-$CUR_DATE-$SHA_SHORT" + export STAGE="${{ inputs.stage }}" + export APP_DIR="$PWD/${{ inputs.dockerfile_location }}" + image_name="${{ inputs.ecr_image_repo_name }}" version="$VERSION" account="${{ inputs.aws_account_id }}" app_dir="$APP_DIR" publish="${{ inputs.should_publish }}" ./bin/dockerize + echo "Docker tag is: $VERSION" + echo $VERSION > /tmp/.DOCKER_IMAGE_VERSION diff --git a/.github/actions/install_cognee/action.yml b/.github/actions/install_cognee/action.yml new file mode 100644 index 0000000..78c8d00 --- /dev/null +++ b/.github/actions/install_cognee/action.yml @@ -0,0 +1,55 @@ +name: install_cognee +description: "Installs Cognee from local source or PyPI depending on specified version" + +inputs: + python-version: + description: "Which Python version to use" + required: false + default: "3.11.x" + cognee_version: + description: "Pypi-compatible version of cognee to use. For example, 0.5.2.dev0. `local` (default) - Installing Cognee from local source" + required: false + default: "local" + +runs: + using: "composite" + steps: + + - name: Detect CI container + id: detect-env + shell: bash + run: | + if [ -f "/app/.anon_id" ] && command -v uv &> /dev/null && [ -d "/app/.venv" ]; then + echo "in-container=true" >> "$GITHUB_OUTPUT" + else + echo "in-container=false" >> "$GITHUB_OUTPUT" + fi + + - name: Set up Python + if: steps.detect-env.outputs.in-container != 'true' + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Install uv + if: steps.detect-env.outputs.in-container != 'true' + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + + - name: (Re)Install Cognee + shell: bash + working-directory: cognee-mcp + env: + VIRTUAL_ENV: ${{ steps.detect-env.outputs.in-container == 'true' && '/app/.venv' || '' }} + run: | + uv pip uninstall cognee + if [ "${{ inputs.cognee_version }}" = "local" ]; then + echo "Installing Cognee from local source ..." + uv pip install --force-reinstall -e ../ + else + echo "Installing Cognee version ${{ inputs.cognee_version }} from PyPI..." + uv pip install --force-reinstall "cognee==${{ inputs.cognee_version }}" + fi + echo "Installed Cognee version:" + uv pip show cognee | grep Version diff --git a/.github/actions/setup_neo4j/action.yml b/.github/actions/setup_neo4j/action.yml new file mode 100644 index 0000000..8685efc --- /dev/null +++ b/.github/actions/setup_neo4j/action.yml @@ -0,0 +1,67 @@ +name: 'Setup Neo4j with Graph Data Science' +description: 'Sets up a Neo4j instance with APOC and Graph Data Science plugins for testing' +inputs: + neo4j-version: + description: 'Neo4j version to use' + required: false + default: '5.21' + neo4j-password: + description: 'Password for Neo4j' + required: false + default: 'cognee_test_password' +outputs: + neo4j-url: + description: 'Neo4j connection URL' + value: 'bolt://localhost:7687' + neo4j-username: + description: 'Neo4j username' + value: 'neo4j' + neo4j-password: + description: 'Neo4j password' + value: ${{ inputs.neo4j-password }} +runs: + using: 'composite' + steps: + - name: Start Neo4j with GDS + shell: bash + run: | + docker run -d \ + --name neo4j-test \ + -p 7474:7474 -p 7687:7687 \ + -e NEO4J_AUTH="neo4j/${{ inputs.neo4j-password }}" \ + -e NEO4J_PLUGINS='["apoc", "graph-data-science"]' \ + -e NEO4J_dbms_security_procedures_unrestricted="apoc.*,gds.*" \ + -e NEO4J_apoc_export_file_enabled=true \ + -e NEO4J_apoc_import_file_enabled=true \ + neo4j:${{ inputs.neo4j-version }} + + - name: Wait for Neo4j to be ready + shell: bash + run: | + echo "Waiting for Neo4j to start..." + timeout=60 + counter=0 + + while [ $counter -lt $timeout ]; do + if docker exec neo4j-test cypher-shell -u neo4j -p "${{ inputs.neo4j-password }}" "RETURN 1" > /dev/null 2>&1; then + echo "Neo4j is ready!" + break + fi + echo "Waiting... ($counter/$timeout)" + sleep 2 + counter=$((counter + 2)) + done + + if [ $counter -ge $timeout ]; then + echo "Neo4j failed to start within $timeout seconds" + docker logs neo4j-test + exit 1 + fi + + - name: Verify GDS is available + shell: bash + run: | + echo "Verifying Graph Data Science library is available..." + docker exec neo4j-test cypher-shell -u neo4j -p "${{ inputs.neo4j-password }}" \ + "CALL gds.version() YIELD gdsVersion RETURN gdsVersion" + echo "GDS verification complete!" diff --git a/.github/core-team.txt b/.github/core-team.txt new file mode 100644 index 0000000..8d91b96 --- /dev/null +++ b/.github/core-team.txt @@ -0,0 +1,11 @@ +# Core team GitHub logins (one per line). Lines may begin with @; case-insensitive. +borisarzentar +daukadolt +dexters1 +hajdul88 +hande-k +lxobr +pazone +siillee +vasilije1990 +NMZivkovic diff --git a/.github/docs/release.md b/.github/docs/release.md new file mode 100644 index 0000000..9013c91 --- /dev/null +++ b/.github/docs/release.md @@ -0,0 +1,33 @@ +## How to make a release + +### Dev release + +#### Prepare release +1. Set the project version that will be released in [pyproject.toml](https://github.com/topoteretes/cognee/blob/dev/pyproject.toml#L4) +2. Update `uv.lock` with `uv lock` lock command +3. Update `poetry.lock` by deleting the `poetry.lock` file first and then running the `poetry lock` command. This is required to update the `poetry.lock` file with the new version of the dependencies, otherwise it does not properly update and mostly uses the cached dependency versions in the existing `poetry.lock` file. +4. Create a PR with the changes mentioned above to `dev` and merge it. + +#### Perform Release +1. Go to [Release action](https://github.com/topoteretes/cognee/actions/workflows/release.yml) +2. Select `dev` branch and run the workflow. +3. Watch the logs and make sure that everything goes well + +### Main release + +#### Prepare release +1. Set the project version that will be released in [pyproject.toml](https://github.com/topoteretes/cognee/blob/dev/pyproject.toml#L4) +2. Update `uv.lock` with `uv lock` lock command +3. Update `poetry.lock` by deleting the `poetry.lock` file first and then running the `poetry lock` command. This is required to update the `poetry.lock` file with the new version of the dependencies, otherwise it does not properly update and mostly uses the cached dependency versions in the existing `poetry.lock` file. +4. Create a PR with the changes mentioned above to `main` and merge it. + +#### Perform Release +1. Go to [Release action](https://github.com/topoteretes/cognee/actions/workflows/release.yml) +2. Select `main` branch and run the workflow. +3. Watch the logs and make sure that everything goes well + +### Release validation + +1. Make sure that the correct image is published to [Docker Hub](https://hub.docker.com/r/cognee/cognee) +2. Python package is published to [PyPi](https://pypi.org/project/cognee/) +3. Find the created github release in [GitHub releases](https://github.com/topoteretes/cognee/releases). Edit/prettify the release notes if required. diff --git a/.github/prompts/docs_edit.md b/.github/prompts/docs_edit.md new file mode 100644 index 0000000..289573b --- /dev/null +++ b/.github/prompts/docs_edit.md @@ -0,0 +1,34 @@ +You are a documentation improvement agent for the Cognee project. + +Update the existing Cognee documentation to reflect this merged PR. Your job is to document the actual behavior and API changes introduced by this PR, not to make adjacent or generic documentation improvements. + +## Required inputs + +Read these first: + +- Documentation scope plan +- Branch notes +- Documentation assessment + +## Available resources + +- **Documentation repo** (`./docs-repo`): Contains the documentation pages. Use existing `.md` and `.mdx` pages as the primary targets for edits. Read `./docs-repo/docs.json` only if the scope plan requires navigation context. +- **Cognee source code** (current workspace root): Use the source code to verify actual implementation details, defaults, supported options, function signatures, env vars, and behavior. + +## Editing rules + +1. Follow the documentation scope plan. +2. If the scope plan says `Docs Needed` is `false`, make no documentation edits and print the reason. +3. Inspect only the source files listed in the scope plan unless they are insufficient to verify a specific planned edit. +4. Edit only docs files listed in the scope plan unless they are clearly the wrong target; if so, choose the smallest better existing docs target. +5. Document only user-facing behavior, public API changes, examples, or developer-facing semantics that actually changed in this PR. +6. If a proposed docs edit cannot be traced back to a concrete source diff in this PR, do not make that edit. +7. Do not present pre-existing behavior as if this PR introduced it. +8. Do not make unrelated cleanup edits, style edits, or generic improvements. +9. Edit at most 3 documentation files unless the scope plan explicitly justifies more. +10. Prefer updating existing pages over creating new ones. +11. Do not edit `docs.json` unless the scope plan says a new docs page is strictly required. +12. Only edit documentation files inside `./docs-repo`. Do NOT modify the source repository files, workflow files, or non-documentation assets. +13. Do NOT create git commits. + +When done, print a short summary of what you changed and which existing documentation pages you updated. diff --git a/.github/prompts/docs_scope_plan.md b/.github/prompts/docs_scope_plan.md new file mode 100644 index 0000000..33c4c33 --- /dev/null +++ b/.github/prompts/docs_scope_plan.md @@ -0,0 +1,46 @@ +You are a documentation scope planner for the Cognee project. + +Analyze this merged PR and produce a small documentation edit plan. Do not edit documentation files. + +## Available resources + +- **Documentation repo** (`./docs-repo`): Contains the documentation pages. Use existing `.md` and `.mdx` pages as the primary targets for edits. Read `./docs-repo/docs.json` if it exists to understand the documentation structure. +- **Cognee source code** (current workspace root): Use the source code to verify actual implementation details, defaults, supported options, function signatures, env vars, and behavior. +- **Prepared documentation edit scope**: Curated source files, docs candidates, documentation signals, assessment summary, and out-of-scope files produced by the workflow. + +## Planning task + +1. Read the prepared documentation edit scope first. +2. Use the prepared scope as your primary evidence. Do not re-classify the full PR changed-file list. +3. Read only the source files listed in `Source Files To Inspect` unless one listed file is insufficient to verify a specific planned edit. +4. Read only the documentation files listed in `Candidate Documentation Files` unless they are clearly the wrong target. +5. Do not run shell commands or inspect the full diff. If the prepared scope is still too broad, produce a conservative small plan instead of exploring further. +6. Treat files listed in `Out Of Scope Files` as skipped unless one is explicitly needed to verify a planned edit. +7. Identify the smallest docs edit surface that could cover the public-facing changes. + +Write the final plan to the scope plan output path provided by the workflow prompt. + +The plan must be Markdown with these exact sections: + +# Documentation Scope Plan + +## Docs Needed +`true` or `false` + +## Reason +One concise paragraph. + +## Documentation-Worthy Changes +Bullets. Each bullet must name the change, the source files proving it, and the recommended docs page type. + +## Files To Edit +Bullets of existing docs files to edit. Use paths relative to `docs-repo`. Leave empty if none. + +## Source Files To Inspect During Editing +Bullets of source files the editing step should inspect. Keep this list short and exclude tests/assets/lockfiles. + +## Out Of Scope +Bullets for changes intentionally skipped. + +Do not edit files inside `./docs-repo`. +Do not create commits. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..9d605c4 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,38 @@ + + +## Description + + +## Acceptance Criteria + + +## Type of Change + +- [ ] Bug fix (non-breaking change that fixes an issue) +- [ ] New feature (non-breaking change that adds functionality) +- [ ] Code refactoring +- [ ] Other (please specify): + +## Screenshots + + +## Pre-submission Checklist + +- [ ] **I have tested my changes thoroughly before submitting this PR** (See `CONTRIBUTING.md`) +- [ ] **This PR contains minimal changes necessary to address the issue/feature** +- [ ] My code follows the project's coding standards and style guidelines +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] I have added necessary documentation (if applicable) +- [ ] All new and existing tests pass +- [ ] I have searched existing PRs to ensure this change hasn't been submitted already +- [ ] I have linked any relevant issues in the description +- [ ] My commits have clear and descriptive messages + +## DCO Affirmation +I affirm that all code in every commit of this pull request conforms to the terms of the Topoteretes Developer Certificate of Origin. diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..1df30ce --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,20 @@ +name-template: 'v$NEXT_PATCH_VERSION' +tag-template: 'v$NEXT_PATCH_VERSION' + +categories: + - title: 'Features' + labels: ['feature', 'enhancement'] + - title: 'Bug Fixes' + labels: ['bug', 'fix'] + - title: 'Maintenance' + labels: ['chore', 'refactor', 'ci'] + +change-template: '- $TITLE (#$NUMBER) @$AUTHOR' +template: | + ## What’s Changed + + $CHANGES + + ## Contributors + + $CONTRIBUTORS diff --git a/.github/workflows/adapter_caching_tests.yml b/.github/workflows/adapter_caching_tests.yml new file mode 100644 index 0000000..a11964c --- /dev/null +++ b/.github/workflows/adapter_caching_tests.yml @@ -0,0 +1,291 @@ +name: Reusable Adapter Caching Tests + +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + databases: + required: false + type: string + default: "all" + description: "Which provider combos to test (comma-separated list or 'all')" + python-version: + required: false + type: string + default: '3.11.x' + description: "Python version to test" + ci-image: + required: false + type: string + default: '' + description: "Container image for CI (empty string means no container)" + secrets: + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + +jobs: + run-ladybug-lance-sqlite-caching: + name: Adapter caching — Ladybug + LanceDB + Sqlite + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'ladybug/lance/sqlite') }} + steps: + - name: Check out + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Run adapter caching test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'ladybug' + VECTOR_DB_PROVIDER: 'lancedb' + DB_PROVIDER: 'sqlite' + run: uv run python ./cognee/tests/test_adapter_object_caching.py + + run-neo4j-lance-sqlite-caching: + name: Adapter caching — Neo4j + LanceDB + Sqlite + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'neo4j/lance/sqlite') }} + steps: + - name: Check out + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run adapter caching test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'neo4j' + VECTOR_DB_PROVIDER: 'lancedb' + DB_PROVIDER: 'sqlite' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + run: uv run python ./cognee/tests/test_adapter_object_caching.py + + run-ladybug-pgvector-postgres-caching: + name: Adapter caching — Ladybug + PGVector + Postgres + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'ladybug/pgvector/postgres') }} + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Run adapter caching test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'ladybug' + VECTOR_DB_PROVIDER: 'pgvector' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_adapter_object_caching.py + + run-neo4j-pgvector-postgres-caching: + name: Adapter caching — Neo4j + PGVector + Postgres + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'neo4j/pgvector/postgres') }} + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries=5 + steps: + - name: Check out + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run adapter caching test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'neo4j' + VECTOR_DB_PROVIDER: 'pgvector' + DB_PROVIDER: 'postgres' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + DB_NAME: cognee_db + DB_HOST: 127.0.0.1 + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_adapter_object_caching.py + + run-postgres-pgvector-postgres-caching: + name: Adapter caching — Postgres graph + PGVector + Postgres (access control) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'postgres/pgvector/postgres') }} + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Run adapter caching test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + # Access control mode: every dataset gets its own physical Postgres + # database (named after the deterministic dataset id) that is created + # on add and dropped on delete — the engine-cache lifecycle across + # that drop/recreate boundary is what this job guards. + ENABLE_BACKEND_ACCESS_CONTROL: 'true' + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + VECTOR_DB_PROVIDER: 'pgvector' + VECTOR_DB_NAME: 'cognee_db' + VECTOR_DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + VECTOR_DB_PORT: 5432 + VECTOR_DB_USERNAME: cognee + VECTOR_DB_PASSWORD: cognee + VECTOR_DATASET_DATABASE_HANDLER: 'pgvector' + GRAPH_DATABASE_PROVIDER: 'postgres' + GRAPH_DATABASE_NAME: 'cognee_db' + GRAPH_DATABASE_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + GRAPH_DATABASE_PORT: 5432 + GRAPH_DATABASE_USERNAME: cognee + GRAPH_DATABASE_PASSWORD: cognee + GRAPH_DATASET_DATABASE_HANDLER: 'postgres_graph' + run: uv run python ./cognee/tests/test_adapter_object_caching.py diff --git a/.github/workflows/approve_dco.yaml b/.github/workflows/approve_dco.yaml new file mode 100644 index 0000000..f7615aa --- /dev/null +++ b/.github/workflows/approve_dco.yaml @@ -0,0 +1,40 @@ +name: community | DCO Check + +on: + pull_request: + types: [opened, edited, reopened, synchronize, ready_for_review] + +jobs: + check-dco: + runs-on: ubuntu-latest + steps: + - name: Validate Developer Certificate of Origin statement + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prUser = context.payload.pull_request.user.login; + const prBody = context.payload.pull_request.body || ''; + const authorAssociation = context.payload.pull_request.author_association; + + // Exact text you require in the PR body + const requiredStatement = "I affirm that all code in every commit of this pull request conforms to the terms of the Topoteretes Developer Certificate of Origin"; + + // 1. Check if user is an org member via author_association + // (available on the PR payload without extra API calls or permissions) + // OWNER, MEMBER, and COLLABORATOR are trusted roles. + const trustedRoles = ['OWNER', 'MEMBER', 'COLLABORATOR', 'CONTRIBUTOR']; + const isTrusted = trustedRoles.includes(authorAssociation); + + if (isTrusted) { + console.log(`${prUser} has association '${authorAssociation}'. Skipping DCO check.`); + } else { + console.log(`${prUser} has association '${authorAssociation}'. Enforcing DCO check.`); + + // 2. If user is not trusted, enforce the DCO statement + if (!prBody.includes(requiredStatement)) { + core.setFailed( + `DCO check failed. The PR body must include the following statement:\n\n${requiredStatement}` + ); + } + } diff --git a/.github/workflows/backend_docker_build_test.yml b/.github/workflows/backend_docker_build_test.yml new file mode 100644 index 0000000..49c1521 --- /dev/null +++ b/.github/workflows/backend_docker_build_test.yml @@ -0,0 +1,27 @@ +name: build test | Docker image + +on: + workflow_call: + +env: + AWS_ACCOUNT_ID_DEV: "463722570299" + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + + build_docker: + name: Build Cognee Backend Docker App Image + runs-on: ubuntu-22.04 + steps: + - name: Check out Cognee code + uses: actions/checkout@v6 + + - name: Build Cognee Docker image + id: cognee-docker-tag + run: | + export SHA_SHORT="$(git rev-parse --short HEAD)" + export CUR_DATE="$(date +%Y%m%d%H%M%S)" + export VERSION="dev-$CUR_DATE-$SHA_SHORT" + image_name="cognee" docker_login="false" version="$VERSION" account="${{ env.AWS_ACCOUNT_ID_DEV }}" app_dir="." publish="false" ./bin/dockerize + export DOCKER_TAG=$(cat /tmp/.DOCKER_IMAGE_VERSION) + echo "Successfully built cognee Docker image. Tag is: $DOCKER_TAG" diff --git a/.github/workflows/basic_tests.yml b/.github/workflows/basic_tests.yml new file mode 100644 index 0000000..5ca79dc --- /dev/null +++ b/.github/workflows/basic_tests.yml @@ -0,0 +1,139 @@ +name: Reusable Basic Tests + +on: + workflow_call: + inputs: + python-version: + required: false + type: string + default: '3.11.x' + ci-image: + required: false + type: string + default: '' + secrets: + LLM_PROVIDER: + required: true + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_PROVIDER: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + unit-tests: + name: Run Unit Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Run Unit Tests + run: uv run pytest cognee/tests/unit/ --timeout=300 --timeout-method=thread + + simple-examples: + name: Run Simple Examples + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Run Simple Examples + run: uv run python ./examples/demos/simple_cognee_example.py + + simple-examples-baml: + name: Run Simple Examples BAML + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + STRUCTURED_OUTPUT_FRAMEWORK: "BAML" + BAML_LLM_PROVIDER: openai + BAML_LLM_MODEL: ${{ secrets.OPENAI_MODEL }} + BAML_LLM_ENDPOINT: ${{ secrets.OPENAI_ENDPOINT }} + BAML_LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} +# BAML_LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "baml" + + - name: Run Simple Examples + run: uv run python ./examples/demos/simple_cognee_example.py diff --git a/.github/workflows/clean_stale_pr.yaml b/.github/workflows/clean_stale_pr.yaml new file mode 100644 index 0000000..04e652d --- /dev/null +++ b/.github/workflows/clean_stale_pr.yaml @@ -0,0 +1,32 @@ +name: clean | remove stale PRs + +on: + # Run this action periodically (daily at 0:00 UTC). + schedule: + - cron: "0 0 * * *" + # Optionally, also run when pull requests are labeled, unlabeled, synchronized, or reopened + # to update the stale timer as needed. Uncomment if desired. + # pull_request: + # types: [labeled, unlabeled, synchronize, reopened] + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Mark and Close Stale PRs + uses: actions/stale@v10 + with: + # Number of days of inactivity before the pull request is marked stale + days-before-stale: 60 + # Number of days of inactivity after being marked stale before the pull request is closed + days-before-close: 7 + # Comment to post when marking as stale + stale-pr-message: "This pull request has been automatically marked as stale due to inactivity. It will be closed in 7 days if no further activity occurs." + # Comment to post when closing a stale pull request + close-pr-message: "This pull request has been closed due to prolonged inactivity." + # Labels for stale and closed PRs + stale-pr-label: "stale" + exempt-pr-labels: "keep-open" diff --git a/.github/workflows/cli_tests.yml b/.github/workflows/cli_tests.yml new file mode 100644 index 0000000..870eaea --- /dev/null +++ b/.github/workflows/cli_tests.yml @@ -0,0 +1,171 @@ +name: test | cli + +on: + workflow_call: + inputs: + python-version: + required: false + type: string + default: '3.11.x' + cognee_version: + required: false + type: string + default: "local" + description: "Pypi-compatible version of cognee to use. For example, 0.5.2.dev0. `local` (default) - Installing Cognee from local source" + ci-image: + required: false + type: string + default: '' + secrets: + LLM_PROVIDER: + required: true + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_PROVIDER: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + cli-unit-tests: + name: CLI Unit Tests + if: ${{ inputs.cognee_version == 'local' }} + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Run CLI Unit Tests + run: uv run pytest cognee/tests/cli_tests/cli_unit_tests/ -v + + cli-integration-tests: + name: CLI Integration Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Install Cognee + uses: ./.github/actions/install_cognee + with: + python-version: ${{ inputs.python-version }} + cognee_version: ${{ inputs.cognee_version }} + + - name: Run CLI Integration Tests + run: uv run pytest cognee/tests/cli_tests/cli_integration_tests/ -v + + cli-functionality-tests: + name: CLI Functionality Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Install Cognee + uses: ./.github/actions/install_cognee + with: + python-version: ${{ inputs.python-version }} + cognee_version: ${{ inputs.cognee_version }} + + - name: Test CLI Help Commands + run: | + uv run python -m cognee.cli._cognee --help + uv run python -m cognee.cli._cognee --version + uv run python -m cognee.cli._cognee add --help + uv run python -m cognee.cli._cognee search --help + uv run python -m cognee.cli._cognee cognify --help + uv run python -m cognee.cli._cognee delete --help + uv run python -m cognee.cli._cognee config --help + + - name: Test CLI Config Subcommands + run: | + uv run python -m cognee.cli._cognee config get --help + uv run python -m cognee.cli._cognee config set --help + uv run python -m cognee.cli._cognee config list --help + uv run python -m cognee.cli._cognee config unset --help + uv run python -m cognee.cli._cognee config reset --help + + - name: Test CLI Error Handling + run: | + # Test invalid command (should fail gracefully) + ! uv run python -m cognee.cli._cognee invalid_command + + # Test missing required arguments (should fail gracefully) + ! uv run python -m cognee.cli._cognee search + + # Test invalid search type (should fail gracefully) + ! uv run python -m cognee.cli._cognee search "test" --query-type INVALID_TYPE + + # Test invalid chunker (should fail gracefully) + ! uv run python -m cognee.cli._cognee cognify --chunker InvalidChunker diff --git a/.github/workflows/community_greetings.yml b/.github/workflows/community_greetings.yml new file mode 100644 index 0000000..42a4398 --- /dev/null +++ b/.github/workflows/community_greetings.yml @@ -0,0 +1,25 @@ +name: community | Greetings + +on: + issues: + types: [opened] + pull_request_target: + types: [opened] + +permissions: + issues: write + pull-requests: write + +jobs: + greeting: + if: github.actor != 'github-actions[bot]' && github.actor != 'dependabot[bot]' && !contains(github.event.pull_request.body, 'Generated with [Claude Code]') + runs-on: ubuntu-22.04 + steps: + - uses: actions/first-interaction@v3 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + pr_message: 'Hello @${{ github.actor }}, thank you for submitting a PR! We will respond as soon as possible.' + issue_message: | + Hello @${{ github.actor }}, thank you for your interest in our work! + + If this is a bug report, please provide screenshots and **minimum viable code to reproduce your issue**, otherwise we can not help you. diff --git a/.github/workflows/community_tests.yml b/.github/workflows/community_tests.yml new file mode 100644 index 0000000..eab50e9 --- /dev/null +++ b/.github/workflows/community_tests.yml @@ -0,0 +1,180 @@ +name: Community Tests (No Secrets) + +# Runs on ALL pull requests, including forks. +# No secrets required — only fully mocked unit tests and code quality checks. +# This gives community contributors fast feedback on their changes. + +on: + pull_request: + branches: [ main, dev ] + types: [opened, synchronize, reopened] + +permissions: + contents: read + +concurrency: + group: community-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + +jobs: + code-quality: + name: Code Quality + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Validate uv lockfile + run: uv lock --check || { echo "'uv lock --check' failed. Run 'uv lock' and push your changes."; exit 1; } + + - name: Run pre-commit hooks + uses: pre-commit/action@v3.0.1 + + - name: Install dependencies + run: uv sync --extra dev + + - name: Ruff check + run: uv run ruff check . + + - name: Ruff format check + run: uv run ruff format --check . + + - name: Ty type check + run: uv run ty check . + + unit-tests: + name: Unit Tests (Mocked, No Secrets) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + python-version: ['3.11.x'] + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Install dependencies + # postgres is needed so the SQL cache adapter tests run instead of skipping. + run: uv sync --extra dev --extra postgres + + - name: Run mocked unit tests + run: | + uv run pytest \ + cognee/tests/unit/processing/ \ + cognee/tests/unit/entity_extraction/ \ + cognee/tests/unit/modules/graph/ \ + cognee/tests/unit/modules/chunking/ \ + cognee/tests/unit/modules/data/ \ + cognee/tests/unit/modules/retrieval/ \ + --ignore=cognee/tests/unit/modules/retrieval/graph_completion_retriever_cot_test.py \ + --ignore=cognee/tests/unit/modules/retrieval/temporal_retriever_test.py \ + --ignore=cognee/tests/unit/modules/retrieval/triplet_retriever_test.py \ + cognee/tests/unit/modules/search/ \ + cognee/tests/unit/modules/pipelines/ \ + cognee/tests/unit/modules/visualization/ \ + cognee/tests/unit/modules/ontology/ \ + cognee/tests/unit/modules/observability/ \ + cognee/tests/unit/modules/users/ \ + cognee/tests/unit/modules/memify_tasks/ \ + cognee/tests/unit/interfaces/graph/ \ + cognee/tests/unit/infrastructure/databases/cache/ \ + cognee/tests/unit/infrastructure/databases/vector/test_vector_db_config.py \ + cognee/tests/unit/infrastructure/databases/relational/ \ + cognee/tests/unit/infrastructure/databases/test_unified_store_engine.py \ + cognee/tests/unit/infrastructure/session/ \ + cognee/tests/unit/infrastructure/llm/ \ + cognee/tests/unit/infrastructure/test_rate_limiting_retry.py \ + cognee/tests/unit/tasks/storage/test_add_data_points.py \ + cognee/tests/unit/shared/ \ + -v --tb=short + + cli-unit-tests: + name: CLI Unit Tests (Mocked, No Secrets) + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11.x' + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync --extra dev + + - name: Run CLI unit tests + run: uv run pytest cognee/tests/cli_tests/cli_unit_tests/ -v --tb=short + + telemetry-test: + name: Telemetry Test (Mocked, No Secrets) + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11.x' + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Install dependencies + run: uv sync --extra dev + + - name: Run telemetry test + run: uv run pytest cognee/tests/test_telemetry.py -v --tb=short + + notify: + name: Community Tests Status + needs: [code-quality, unit-tests, cli-unit-tests, telemetry-test] + runs-on: ubuntu-latest + if: ${{ !cancelled() }} + steps: + - name: Check Status + run: | + if [[ "${{ needs.code-quality.result }}" == "success" && + "${{ needs.unit-tests.result }}" == "success" && + "${{ needs.cli-unit-tests.result }}" == "success" && + "${{ needs.telemetry-test.result }}" == "success" ]]; then + echo "All community tests passed!" + else + echo "One or more community tests failed." + exit 1 + fi diff --git a/.github/workflows/db_examples_tests.yml b/.github/workflows/db_examples_tests.yml new file mode 100644 index 0000000..232f14f --- /dev/null +++ b/.github/workflows/db_examples_tests.yml @@ -0,0 +1,165 @@ +name: Reusable DB Examples Tests + +on: + workflow_call: + inputs: + databases: + required: false + type: string + default: "all" + description: "Which databases to run (comma-separated or 'all')" + python-version: + required: false + type: string + default: "3.11.x" + ci-image: + required: false + type: string + default: '' + secrets: + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + + POSTGRES_PASSWORD: + required: false + NEO4J_API_URL: + required: false + NEO4J_API_KEY: + required: false + + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + run-db-example-neo4j: + name: "Neo4j DB Example Test" + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'neo4j') }} + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run Neo4j Example + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + run: | + uv run python examples/database_examples/neo4j_example.py + + run-db-example-ladybug: + name: "Ladybug DB Example Test" + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'ladybug') || contains(inputs.databases, 'kuzu') }} + + steps: + - name: Check out + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Ladybug Example + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "ladybug" + run: | + uv run python examples/database_examples/ladybug_example.py + + run-db-example-pgvector: + name: "PostgreSQL PGVector DB Example Test" + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'postgres') }} + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Run PGVector Example + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + run: | + uv run python examples/database_examples/pgvector_example.py diff --git a/.github/workflows/dev_canary_release.yml b/.github/workflows/dev_canary_release.yml new file mode 100644 index 0000000..18c0af9 --- /dev/null +++ b/.github/workflows/dev_canary_release.yml @@ -0,0 +1,123 @@ +name: Dev Canary Release + +on: + schedule: + # Every Monday at 06:00 UTC + - cron: "0 6 * * 1" + workflow_dispatch: + workflow_call: + +concurrency: + group: dev-canary-release + cancel-in-progress: true + +permissions: + contents: read + packages: write + +jobs: + # ── Gate: run core test suites before publishing ──────────────────── + test-gate: + name: Canary Test Gate + uses: ./.github/workflows/basic_tests.yml + with: + ci-image: "" + secrets: inherit + + # ── Compute canary version ────────────────────────────────────────── + prepare: + name: Prepare Canary Version + runs-on: ubuntu-latest + needs: test-gate + outputs: + version: ${{ steps.version.outputs.version }} + canary_version: ${{ steps.version.outputs.canary_version }} + steps: + - name: Check out dev + uses: actions/checkout@v6 + with: + ref: dev + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Compute canary version + id: version + run: | + BASE_VERSION="$(uv version --short)" + # Strip any existing .devN suffix to get the base + CLEAN_VERSION="${BASE_VERSION%%\.dev*}" + # PEP 440 dev release: 0.5.4.dev20260309 + CANARY_VERSION="${CLEAN_VERSION}.dev$(date -u +%Y%m%d)" + echo "version=${CLEAN_VERSION}" >> "$GITHUB_OUTPUT" + echo "canary_version=${CANARY_VERSION}" >> "$GITHUB_OUTPUT" + echo "Canary version: ${CANARY_VERSION}" + + # ── Publish to PyPI ───────────────────────────────────────────────── + release-pypi: + name: Publish Dev Canary to PyPI + needs: prepare + runs-on: ubuntu-latest + steps: + - name: Check out dev + uses: actions/checkout@v6 + with: + ref: dev + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Set canary version in pyproject.toml + run: uv version "${{ needs.prepare.outputs.canary_version }}" + + - name: Install dependencies + run: uv sync --all-extras + + - name: Build distributions + run: uv build + + - name: Publish to PyPI + env: + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} + run: uv publish + + # ── Publish Docker image ──────────────────────────────────────────── + release-docker: + name: Publish Dev Canary Docker Image + needs: prepare + runs-on: ubuntu-latest + steps: + - name: Check out dev + uses: actions/checkout@v6 + with: + ref: dev + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: | + cognee/cognee:dev-canary + cognee/cognee:${{ needs.prepare.outputs.canary_version }} + labels: | + version=${{ needs.prepare.outputs.canary_version }} + flavour=dev-canary + cache-from: type=registry,ref=cognee/cognee:buildcache + cache-to: type=registry,ref=cognee/cognee:buildcache,mode=max diff --git a/.github/workflows/dev_previous_day_commits.yml b/.github/workflows/dev_previous_day_commits.yml new file mode 100644 index 0000000..e6b2b0c --- /dev/null +++ b/.github/workflows/dev_previous_day_commits.yml @@ -0,0 +1,351 @@ +name: automation | Draft Docs PRs For Previous Day Dev PRs + +on: + workflow_dispatch: + inputs: + lookback_days: + description: Number of UTC calendar days to look back when scanning merged PRs + required: false + default: "1" + anchor_date: + description: Optional UTC date to scan, in YYYY-MM-DD format + required: false + default: "" + schedule: + - cron: "59 23 * * *" + +permissions: + contents: read + pull-requests: read + +env: + lookback_days: ${{ github.event.inputs.lookback_days || vars.lookback_days || '1' }} + +jobs: + prepare-merged-branches: + runs-on: ubuntu-22.04 + timeout-minutes: 10 + outputs: + start_date: ${{ steps.prepare.outputs.start_date }} + end_date: ${{ steps.prepare.outputs.end_date }} + has_merges: ${{ steps.prepare.outputs.has_merges }} + merge_summary: ${{ steps.prepare.outputs.merge_summary }} + matrix: ${{ steps.prepare.outputs.matrix }} + + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Fetch latest dev branch + run: git fetch origin dev:refs/remotes/origin/dev --no-tags + + - name: Prepare merged PRs + id: prepare + shell: bash + env: + INPUT_ANCHOR_DATE: ${{ github.event.inputs.anchor_date || '' }} + GITHUB_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + args=( + --branch origin/dev + --lookback-days "${lookback_days}" + ) + + anchor_date="${INPUT_ANCHOR_DATE}" + if [ "${{ github.event_name }}" = "schedule" ]; then + anchor_date="$(date -u -d 'yesterday' +%F)" + fi + + if [ -n "${anchor_date}" ]; then + echo "Using anchor date ${anchor_date}" + args+=(--anchor-date "${anchor_date}") + fi + + python3 tools/prepare_merged_branches.py "${args[@]}" + + create-docs-prs: + needs: + - prepare-merged-branches + if: ${{ needs.prepare-merged-branches.outputs.has_merges == 'true' }} + runs-on: ubuntu-22.04 + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(needs.prepare-merged-branches.outputs.matrix) }} + + steps: + - name: Check out core repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Set up uv + uses: astral-sh/setup-uv@v7 + + - name: Install workflow dependencies + run: uv sync --locked + + - name: Fetch latest dev branch + run: git fetch origin dev:refs/remotes/origin/dev --no-tags + + - name: Prepare branch note paths + id: branch_paths + run: | + OUTPUT_DIR="branch-dev-notes/${{ matrix.safe_branch }}-${{ matrix.short_sha }}" + mkdir -p "${OUTPUT_DIR}" + echo "output_dir=${OUTPUT_DIR}" >> "${GITHUB_OUTPUT}" + echo "notes_json=${OUTPUT_DIR}/branch_notes.json" >> "${GITHUB_OUTPUT}" + echo "notes_markdown=${OUTPUT_DIR}/branch_notes.md" >> "${GITHUB_OUTPUT}" + echo "assessment_json=${OUTPUT_DIR}/docs_assessment.json" >> "${GITHUB_OUTPUT}" + echo "assessment_markdown=${OUTPUT_DIR}/docs_assessment.md" >> "${GITHUB_OUTPUT}" + echo "docs_edit_scope_json=${OUTPUT_DIR}/docs_edit_scope.json" >> "${GITHUB_OUTPUT}" + echo "docs_edit_scope_markdown=${OUTPUT_DIR}/docs_edit_scope.md" >> "${GITHUB_OUTPUT}" + echo "docs_scope_plan=${OUTPUT_DIR}/docs_scope_plan.md" >> "${GITHUB_OUTPUT}" + + - name: Generate branch notes + env: + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY || secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MODEL: ${{ vars.LLM_MODEL || secrets.LLM_MODEL || 'openai/gpt-4o-mini' }} + NOTES_JSON: ${{ steps.branch_paths.outputs.notes_json }} + NOTES_MARKDOWN: ${{ steps.branch_paths.outputs.notes_markdown }} + PR_NUMBER: ${{ matrix.pr_number }} + PR_TITLE: ${{ matrix.pr_title }} + PR_BODY_B64: ${{ matrix.pr_body_b64 }} + PR_URL: ${{ matrix.pr_url }} + run: | + uv run python tools/generate_branch_notes.py \ + --branch-name "${{ matrix.branch_name }}" \ + --merge-sha "${{ matrix.merge_sha }}" \ + --first-parent "${{ matrix.first_parent }}" \ + --second-parent "${{ matrix.second_parent }}" \ + --pr-number "${PR_NUMBER}" \ + --pr-title "${PR_TITLE}" \ + --pr-body-base64 "${PR_BODY_B64}" \ + --pr-url "${PR_URL}" \ + --json-output "${NOTES_JSON}" \ + --markdown-output "${NOTES_MARKDOWN}" + + - name: Assess documentation impact for branch + id: assessment + env: + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY || secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MODEL: ${{ vars.LLM_MODEL || secrets.LLM_MODEL || 'openai/gpt-4o-mini' }} + NOTES_JSON: ${{ steps.branch_paths.outputs.notes_json }} + NOTES_MARKDOWN: ${{ steps.branch_paths.outputs.notes_markdown }} + ASSESSMENT_JSON: ${{ steps.branch_paths.outputs.assessment_json }} + ASSESSMENT_MARKDOWN: ${{ steps.branch_paths.outputs.assessment_markdown }} + run: | + uv run python tools/assess_branch_notes.py \ + --notes-json "${NOTES_JSON}" \ + --notes-markdown "${NOTES_MARKDOWN}" \ + --json-output "${ASSESSMENT_JSON}" \ + --markdown-output "${ASSESSMENT_MARKDOWN}" + + python3 tools/write_docs_assessment_outputs.py \ + --assessment-json "${ASSESSMENT_JSON}" \ + --branch-slug "${{ matrix.safe_branch }}" \ + --short-sha "${{ matrix.short_sha }}" \ + --pr-number "${{ matrix.pr_number }}" + + - name: Check out docs repository + if: ${{ steps.assessment.outputs.needs_update == 'true' }} + uses: actions/checkout@v6 + with: + repository: topoteretes/cognee-docs + token: ${{ secrets.REPO_DISPATCH_PAT_TOKEN }} + ref: main + path: docs-repo + + - name: Prepare docs branch + if: ${{ steps.assessment.outputs.needs_update == 'true' }} + working-directory: docs-repo + run: | + git fetch origin "${{ steps.assessment.outputs.docs_branch }}" || true + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + if git show-ref --verify --quiet "refs/remotes/origin/${{ steps.assessment.outputs.docs_branch }}"; then + git checkout -B "${{ steps.assessment.outputs.docs_branch }}" "origin/${{ steps.assessment.outputs.docs_branch }}" + else + git checkout -B "${{ steps.assessment.outputs.docs_branch }}" + fi + + - name: Prepare docs edit scope + if: ${{ steps.assessment.outputs.needs_update == 'true' }} + id: docs_scope + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + python3 tools/prepare_docs_edit_scope.py \ + --repo "${{ github.repository }}" \ + --pr-number "${{ matrix.pr_number }}" \ + --docs-root docs-repo \ + --notes-json "${{ steps.branch_paths.outputs.notes_json }}" \ + --assessment-json "${{ steps.branch_paths.outputs.assessment_json }}" \ + --scope-json-output "${{ steps.branch_paths.outputs.docs_edit_scope_json }}" \ + --scope-markdown-output "${{ steps.branch_paths.outputs.docs_edit_scope_markdown }}" + + - name: Plan docs changes with Claude + if: ${{ steps.assessment.outputs.needs_update == 'true' }} + id: claude_scope + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + prompt: | + Read and follow `.github/prompts/docs_scope_plan.md`. + + - Branch: `${{ matrix.branch_name }}` + - PR: `#${{ matrix.pr_number }} ${{ matrix.pr_title }}` + - Merge SHA: `${{ matrix.merge_sha }}` + - Short SHA: `${{ matrix.short_sha }}` + - First parent: `${{ matrix.first_parent }}` + - Second parent: `${{ matrix.second_parent }}` + - Scope plan output: `./${{ steps.branch_paths.outputs.docs_scope_plan }}` + + - **Prepared documentation edit scope** (`./${{ steps.branch_paths.outputs.docs_edit_scope_markdown }}`): Curated source files, docs candidates, assessment summary, and out-of-scope files. + - **Prepared documentation edit scope JSON** (`./${{ steps.branch_paths.outputs.docs_edit_scope_json }}`): Machine-readable copy of the prepared scope. + claude_args: "--allowed-tools Read,Write,Glob,Grep --max-turns 35" + + - name: Validate docs scope plan + if: ${{ steps.assessment.outputs.needs_update == 'true' }} + run: | + test -s "${{ steps.branch_paths.outputs.docs_scope_plan }}" + if [ -n "$(git -C docs-repo status --short)" ]; then + echo "The docs planning step modified docs-repo, but planning must not edit documentation." >&2 + git -C docs-repo status --short >&2 + exit 1 + fi + + - name: Read docs scope plan outputs + if: ${{ steps.assessment.outputs.needs_update == 'true' }} + id: scope_plan + run: | + python3 tools/write_docs_scope_plan_outputs.py \ + --scope-plan "${{ steps.branch_paths.outputs.docs_scope_plan }}" + + - name: Generate docs changes with Claude + if: ${{ steps.assessment.outputs.needs_update == 'true' && steps.scope_plan.outputs.docs_needed == 'true' }} + id: claude + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + prompt: | + Read and follow `.github/prompts/docs_edit.md`. + + - Branch: `${{ matrix.branch_name }}` + - PR: `#${{ matrix.pr_number }} ${{ matrix.pr_title }}` + - Merge SHA: `${{ matrix.merge_sha }}` + - Short SHA: `${{ matrix.short_sha }}` + - First parent: `${{ matrix.first_parent }}` + - Second parent: `${{ matrix.second_parent }}` + + ## Required inputs + + Read these first: + + - **Documentation scope plan** (`./${{ steps.branch_paths.outputs.docs_scope_plan }}`) + - **Branch notes** (`./${{ steps.branch_paths.outputs.notes_markdown }}`) + - **Documentation assessment** (`./${{ steps.branch_paths.outputs.assessment_markdown }}`) + claude_args: "--allowed-tools Read,Edit,Write,Glob,Grep,Bash --max-turns 50" + + - name: Prepare docs PR content + if: ${{ steps.assessment.outputs.needs_update == 'true' && steps.scope_plan.outputs.docs_needed == 'true' }} + id: pr_content + env: + NOTES_JSON: ${{ steps.branch_paths.outputs.notes_json }} + ASSESSMENT_JSON: ${{ steps.branch_paths.outputs.assessment_json }} + BRANCH_NAME: ${{ matrix.branch_name }} + SHORT_SHA: ${{ matrix.short_sha }} + DEFAULT_PR_TITLE: ${{ steps.assessment.outputs.pr_title }} + run: | + python3 tools/prepare_docs_pr_content.py \ + --notes-json "${NOTES_JSON}" \ + --assessment-json "${ASSESSMENT_JSON}" \ + --branch-name "${BRANCH_NAME}" \ + --short-sha "${SHORT_SHA}" \ + --default-pr-title "${DEFAULT_PR_TITLE}" \ + --docs-root docs-repo + + - name: Create docs draft commit + if: ${{ steps.assessment.outputs.needs_update == 'true' && steps.scope_plan.outputs.docs_needed == 'true' }} + id: commit_docs + working-directory: docs-repo + run: | + git add -A + + if git diff --cached --quiet; then + echo "changes_made=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + git commit -m "${{ steps.pr_content.outputs.pr_title }}" + echo "changes_made=true" >> "$GITHUB_OUTPUT" + + - name: Push docs draft branch + if: ${{ steps.assessment.outputs.needs_update == 'true' && steps.scope_plan.outputs.docs_needed == 'true' && steps.commit_docs.outputs.changes_made == 'true' }} + working-directory: docs-repo + run: git push --force-with-lease origin "${{ steps.assessment.outputs.docs_branch }}" + + - name: Create or update docs pull request + if: ${{ steps.assessment.outputs.needs_update == 'true' && steps.scope_plan.outputs.docs_needed == 'true' }} + id: manage_pr + env: + GH_TOKEN: ${{ secrets.REPO_DISPATCH_PAT_TOKEN }} + HEAD_BRANCH: ${{ steps.assessment.outputs.docs_branch }} + PR_TITLE: ${{ steps.pr_content.outputs.pr_title }} + PR_BODY: ${{ steps.pr_content.outputs.pr_body }} + CHANGES_MADE: ${{ steps.commit_docs.outputs.changes_made }} + run: | + python3 tools/manage_docs_pr.py \ + --target-repo topoteretes/cognee-docs \ + --head-branch "${HEAD_BRANCH}" \ + --pr-title "${PR_TITLE}" \ + --pr-body "${PR_BODY}" \ + --changes-made "${CHANGES_MADE}" + + - name: Summarize docs PR result + if: ${{ steps.assessment.outputs.needs_update == 'true' && steps.scope_plan.outputs.docs_needed == 'true' }} + env: + CHANGED_FILES: ${{ steps.pr_content.outputs.changed_files }} + run: | + { + echo "## PR docs review: #${{ matrix.pr_number }} ${{ matrix.pr_title }}" + echo + echo "- Branch: \`${{ matrix.branch_name }}\`" + echo "- Merge commit: \`${{ matrix.merge_sha }}\`" + echo "- Needs documentation update: \`${{ steps.assessment.outputs.needs_update }}\`" + if [ "${{ steps.assessment.outputs.needs_update }}" = "true" ]; then + echo "- Docs branch: \`${{ steps.assessment.outputs.docs_branch }}\`" + if [ -n "${CHANGED_FILES}" ]; then + echo "- Updated docs files:" + printf '%s\n' "${CHANGED_FILES}" | while IFS= read -r changed_file; do + [ -n "${changed_file}" ] || continue + echo " - \`${changed_file}\`" + done + else + echo "- Updated docs files: none" + fi + if [ -n "${{ steps.manage_pr.outputs.pr_url }}" ]; then + echo "- Pull request: ${{ steps.manage_pr.outputs.pr_url }}" + else + echo "- Pull request: not created" + fi + fi + echo + echo "### Documentation assessment" + echo + cat "${{ steps.branch_paths.outputs.assessment_markdown }}" + } >> "${GITHUB_STEP_SUMMARY}" diff --git a/.github/workflows/disable_independent_workflows.sh b/.github/workflows/disable_independent_workflows.sh new file mode 100755 index 0000000..f9e7372 --- /dev/null +++ b/.github/workflows/disable_independent_workflows.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Navigate to the workflows directory +cd "$(dirname "$0")" + +# List of workflows that should only be triggered via test-suites.yml +WORKFLOWS=( + "test_weaviate.yml" + "test_kuzu.yml" + "test_multimetric_qa_eval_run.yaml" + "test_graphrag_vs_rag_notebook.yml" + "test_llms.yml" + "test_multimedia_example.yaml" + "test_deduplication.yml" + "test_eval_framework.yml" + "test_descriptive_graph_metrics.yml" + "test_llama_index_cognee_integration_notebook.yml" + "test_cognee_llama_index_notebook.yml" + "test_cognee_multimedia_notebook.yml" + "test_cognee_server_start.yml" + "test_telemetry.yml" + "test_neo4j.yml" + "test_pgvector.yml" + "test_ollama.yml" + "test_notebook.yml" + "test_simple_example.yml" + "test_code_graph_example.yml" +) + +for workflow in "${WORKFLOWS[@]}"; do + if [ -f "$workflow" ]; then + echo "Processing $workflow..." + + # Create a backup + cp "$workflow" "${workflow}.bak" + + # Check if the file begins with a workflow_call trigger + if grep -q "workflow_call:" "$workflow"; then + echo "$workflow already has workflow_call trigger, skipping..." + continue + fi + + # Get the content after the 'on:' section + on_line=$(grep -n "^on:" "$workflow" | cut -d ':' -f1) + + if [ -z "$on_line" ]; then + echo "Warning: No 'on:' section found in $workflow, skipping..." + continue + fi + + # Create a new file with the modified content + { + # Copy the part before 'on:' + head -n $((on_line-1)) "$workflow" + + # Add the new on: section that only includes workflow_call + echo "on:" + echo " workflow_call:" + echo " secrets:" + echo " inherit: true" + + # Find where to continue after the original 'on:' section + next_section=$(awk "NR > $on_line && /^[a-z]/ {print NR; exit}" "$workflow") + + if [ -z "$next_section" ]; then + next_section=$(wc -l < "$workflow") + next_section=$((next_section+1)) + fi + + # Copy the rest of the file starting from the next section + tail -n +$next_section "$workflow" + } > "${workflow}.new" + + # Replace the original with the new version + mv "${workflow}.new" "$workflow" + + echo "Modified $workflow to only run when called from test-suites.yml" + else + echo "Warning: $workflow not found, skipping..." + fi +done + +echo "Finished modifying workflows!" diff --git a/.github/workflows/distributed_test.yml b/.github/workflows/distributed_test.yml new file mode 100644 index 0000000..d102926 --- /dev/null +++ b/.github/workflows/distributed_test.yml @@ -0,0 +1,81 @@ +name: Distributed Cognee test with modal +permissions: + contents: read + packages: read +on: + workflow_call: + inputs: + python-version: + required: false + type: string + default: '3.13.x' + ci-image: + required: false + type: string + default: '' + secrets: + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + OPENAI_API_KEY: + required: true + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + run-server-start-test: + name: Distributed Cognee test (Modal) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.13.x' + extra-dependencies: "distributed postgres" + + - name: Run Distributed Cognee (Modal) + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }} + MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }} + MODAL_SECRET_NAME: ${{ secrets.MODAL_SECRET_NAME }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ secrets.AZURE_NEO4j_URL }} + GRAPH_DATABASE_USERNAME: ${{ secrets.AZURE_NEO4J_USERNAME }} + GRAPH_DATABASE_PASSWORD: ${{ secrets.AZURE_NEO4J_PW }} + DB_PROVIDER: "postgres" + DB_NAME: ${{ secrets.AZURE_POSTGRES_DB_NAME }} + DB_HOST: ${{ secrets.AZURE_POSTGRES_HOST }} + DB_PORT: ${{ secrets.AZURE_POSTGRES_PORT }} + DB_USERNAME: ${{ secrets.AZURE_POSTGRES_USERNAME }} + DB_PASSWORD: ${{ secrets.AZURE_POSTGRES_PW }} + VECTOR_DB_PROVIDER: "pgvector" + COGNEE_DISTRIBUTED: "true" + run: uv run modal run ./distributed/entrypoint.py diff --git a/.github/workflows/docker_compose.yml b/.github/workflows/docker_compose.yml new file mode 100644 index 0000000..71f6238 --- /dev/null +++ b/.github/workflows/docker_compose.yml @@ -0,0 +1,108 @@ +name: test | docker compose + +on: + workflow_call: + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + docker-compose-test: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout repository + uses: actions/checkout@master + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker images + env: + ENV: dev + run: | + docker compose -f docker-compose.yml build + + - name: Create container .env + # docker-compose.yml bind-mounts ./.env into the container; CI has no + # checked-in .env, so provide the runtime credentials the server needs. + # Secrets flow through the env block so the script body never renders + # secret material (GitHub's log masking then only has exact values to hide). + env: + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: | + printenv | grep -E '^(LLM_|EMBEDDING_)' > .env + echo "EMBEDDING_DIMENSIONS=300" >> .env + echo "COGNEE_SKIP_CONNECTION_TEST=true" >> .env + + - name: Run Docker Compose + env: + ENV: dev + run: | + docker compose -f docker-compose.yml up -d + + - name: Wait for server health + run: | + for attempt in $(seq 1 60); do + if curl -sf http://localhost:8000/health >/dev/null; then + echo "server healthy after ~$((attempt * 3))s" + exit 0 + fi + if [ "$(docker inspect -f '{{.State.Running}}' cognee 2>/dev/null)" != "true" ]; then + echo "cognee container is not running" + docker compose -f docker-compose.yml logs cognee | tail -60 + exit 1 + fi + sleep 3 + done + echo "server did not become healthy in time" + docker compose -f docker-compose.yml logs cognee | tail -60 + exit 1 + + - name: Verify remember and recall round-trip + run: | + TOKEN=$(curl -sf -X POST http://localhost:8000/api/v1/auth/login \ + -d "username=default_user@example.com&password=default_password" | jq -r .access_token) + [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ] || { echo "login failed"; exit 1; } + + FACT="The glassblower Odene Marsk crafted the twin cobalt lanterns of the Vellinge lighthouse in 1931." + echo "$FACT" > fact.txt + REMEMBERED=$(curl -sf -X POST http://localhost:8000/api/v1/remember \ + -H "Authorization: Bearer $TOKEN" \ + -F "datasetName=compose_smoke" \ + -F "data=@fact.txt;type=text/plain") + echo "$REMEMBERED" | jq -e '.dataset_id != null' >/dev/null \ + || { echo "remember returned no dataset_id"; echo "$REMEMBERED"; exit 1; } + echo "remember: ingested into $(echo "$REMEMBERED" | jq -r .dataset_id)" + + # Deterministic check: CHUNKS recall returns the raw stored text, no + # LLM involved — the full stored fact must come back verbatim. + CHUNKS=$(curl -sf -X POST http://localhost:8000/api/v1/recall \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"query":"cobalt lanterns lighthouse","searchType":"CHUNKS","datasets":["compose_smoke"]}') + echo "$CHUNKS" | grep -qF "$FACT" || { echo "CHUNKS recall missing stored fact"; echo "$CHUNKS"; exit 1; } + echo "recall (CHUNKS): stored fact retrieved verbatim" + + # End-to-end answer check: the graph-grounded completion must name the entity. + ANSWER=$(curl -sf -X POST http://localhost:8000/api/v1/recall \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"query":"Who crafted the twin cobalt lanterns of the Vellinge lighthouse, and in which year?","searchType":"GRAPH_COMPLETION","datasets":["compose_smoke"]}') + echo "recall (GRAPH_COMPLETION): $ANSWER" + echo "$ANSWER" | grep -qi "Marsk" || { echo "graph completion did not mention the remembered entity"; exit 1; } + + - name: Show server logs on failure + if: failure() + # Filter key-shaped lines: GitHub masks exact secret values, but partial + # or transformed echoes (e.g. provider auth errors) would slip through. + run: docker compose -f docker-compose.yml logs cognee | grep -viE "api[-_]?key|authorization|bearer" | tail -100 + + - name: Shut down Docker Compose + if: always() + run: | + docker compose -f docker-compose.yml down diff --git a/.github/workflows/dockerhub-mcp.yml b/.github/workflows/dockerhub-mcp.yml new file mode 100644 index 0000000..d37afec --- /dev/null +++ b/.github/workflows/dockerhub-mcp.yml @@ -0,0 +1,65 @@ +name: build | Build and Push Cognee MCP Docker Image to dockerhub + +on: + push: + branches: + - main + +jobs: + docker-build-and-push: + runs-on: + group: Default + labels: + - docker_build_runner + + steps: + - name: Check and free disk space before build + run: | + echo "=== Before cleanup ===" + df -h + echo "Removing unused preinstalled SDKs to free space..." + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc || true + docker system prune -af || true + echo "=== After cleanup ===" + df -h + + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + buildkitd-flags: --root /tmp/buildkit + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: cognee/cognee-mcp + tags: | + type=ref,event=branch + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push + id: build + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + file: cognee-mcp/Dockerfile + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=cognee/cognee-mcp:buildcache + cache-to: type=registry,ref=cognee/cognee-mcp:buildcache,mode=max + + + - name: Image digest + run: echo ${{ steps.build.outputs.digest }} diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml new file mode 100644 index 0000000..f5ca031 --- /dev/null +++ b/.github/workflows/dockerhub.yml @@ -0,0 +1,48 @@ +name: build | Build and Push Docker Image to dockerhub + +on: + push: + branches: + - main + +jobs: + docker-build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: cognee/cognee + tags: | + type=ref,event=branch + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push + id: build + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=cognee/cognee:buildcache + cache-to: type=registry,ref=cognee/cognee:buildcache,mode=max + + - name: Image digest + run: echo ${{ steps.build.outputs.digest }} diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml new file mode 100644 index 0000000..eab0599 --- /dev/null +++ b/.github/workflows/e2e_tests.yml @@ -0,0 +1,2120 @@ +name: Reusable Integration Tests +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + python-version: + required: false + type: string + default: '3.11.x' + ci-image: + required: false + type: string + default: '' + secrets: + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + OPENAI_API_KEY: + required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + run-server-start-test: + name: Server Start Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Server Tests + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_cognee_server_start.py + + run-telemetry-test: + name: Run Telemetry Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Telemetry Tests + env: + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_telemetry.py + + run-telemetry-pipeline-test: + name: Run Telemetry Pipeline Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Add telemetry identifier + run: | + echo "test-machine" > .anon_id + + - name: Run default basic pipeline with telemetry on + env: + ENV: 'local' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_library.py + + run-pipeline-error-propagation-test: + name: Pipeline Error Propagation Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run pipeline error propagation test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_pipeline_error_propagation.py + + run-cogx-roundtrip-test: + name: COGX Roundtrip Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run COGX roundtrip test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_cogx_roundtrip.py + + run-external-source-import-test: + name: External Source Import Test (Zep export -> COGX import -> search) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run external source import test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_external_source_import.py + + run-deduplication-test: + name: Deduplication Test + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Run Deduplication Example + env: + ENV: 'dev' + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} # Test needs OpenAI endpoint to handle multimedia + LLM_ARGS: ${{ secrets.LLM_ARGS }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + run: uv run python ./cognee/tests/test_deduplication.py + + run-s3-bucket-test: + name: S3 Bucket Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "aws" + + - name: Run S3 Bucket Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: uv run python ./cognee/tests/test_s3.py + + test-parallel-databases: + name: Test using different async databases in parallel in Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run parallel databases test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_parallel_databases.py + + test-dataset-database-handler: + name: Test dataset database handlers in Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run dataset databases handler test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_dataset_database_handler.py + + test-dataset-database-deletion: + name: Test dataset database deletion in Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run dataset databases deletion test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_dataset_delete.py + + test-dataset-deletion-when-left-empty: + name: Test dataset deletion when last piece of data deleted + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run dataset deletion test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_data_and_dataset_if_empty.py + + test-permissions: + name: Test permissions with different situations in Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run permissions test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run pytest cognee/tests/test_permissions.py -v --log-level=INFO + + test-agent-memory: + name: Test agent memory end-to-end in Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run agent memory e2e test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + ENABLE_BACKEND_ACCESS_CONTROL: "True" + run: uv run pytest cognee/tests/test_agent_memory_e2e.py -v --log-level=INFO + + test-multi-tenancy: + name: Test multi tenancy with different situations in Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run multi tenancy test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_multi_tenancy.py + + test-data-label: + name: Test adding of label for data in Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run custom data label test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_custom_data_label.py + + test-delete-permission: + name: Test delete permission assigned from dataset owner + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run delete persmission test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_permission.py + + test-deletion-on-default-graph: + name: Delete default graph data test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run deletion on default graph + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_default_graph.py + + test-deletion-on-default-graph-non-mocked: + name: Delete default graph data test (non-mocked) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run deletion on default graph non mocked + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_default_graph_non_mocked.py + + test-deletion-on-custom-graph: + name: Delete custom graph data test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run deletion on custom graph + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_custom_graph.py + + test-delete-two-documents-empty-store: + name: Delete two documents, verify incremental empty store (Ladybug/LanceDB) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run two-document incremental delete (Ladybug/LanceDB) + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_two_documents_empty_store.py + + test-delete-two-documents-empty-store-postgres: + name: Delete two documents, verify incremental empty store (Postgres + PGVector) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Run two-document incremental delete (Postgres graph + PGVector + Postgres relational) + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'postgres' + GRAPH_DATABASE_NAME: 'cognee_db' + GRAPH_DATABASE_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + GRAPH_DATABASE_PORT: 5432 + GRAPH_DATABASE_USERNAME: cognee + GRAPH_DATABASE_PASSWORD: cognee + VECTOR_DB_PROVIDER: 'pgvector' + VECTOR_DB_NAME: 'cognee_db' + VECTOR_DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + VECTOR_DB_PORT: 5432 + VECTOR_DB_USERNAME: cognee + VECTOR_DB_PASSWORD: cognee + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_delete_two_documents_empty_store.py + + test-delete-two-documents-empty-store-neo4j: + name: Delete two documents, verify incremental empty store (Neo4j/LanceDB) + runs-on: ubuntu-22.04 + steps: + - name: Check out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run two-document incremental delete (Neo4j graph + LanceDB) + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + # Neo4j Community has no multi-database support, so run single-tenant + # (one shared graph). The graph-native provenance delete under test is + # independent of per-dataset isolation. + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + run: uv run python ./cognee/tests/test_delete_two_documents_empty_store.py + + # Neo4j jobs - NO container (uses setup_neo4j docker action) + test-deletion-on-default-graph_with_legacy_data_1_neo4j: + name: Delete default graph with legacy data test 1 in Neo4j case + runs-on: ubuntu-22.04 + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run delete dataset test in Neo4j case + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: false + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + ENABLE_AUTO_MIGRATIONS: 'false' + run: uv run python ./cognee/tests/test_delete_default_graph_with_legacy_data_1.py + + test-deletion-on-default-graph_with_legacy_data_2: + name: Delete default graph with legacy data test 2 + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run deletion on custom graph + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + ENABLE_AUTO_MIGRATIONS: 'false' + run: uv run python ./cognee/tests/test_delete_default_graph_with_legacy_data_2.py + + test-delete-two-users-with-legacy-data: + name: Delete data from two users operating on different datasets + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run delete test with two users with legacy data + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + ENABLE_AUTO_MIGRATIONS: 'false' + run: uv run python ./cognee/tests/test_delete_two_users_with_legacy_data.py + + test-delete-two-users-on-same-dataset: + name: Delete data from two users operating on same dataset + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run delete test with two users operating on same dataset + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_two_users_same_dataset.py + + test-delete-dataset-default: + name: Delete dataset in Kuzu graph case + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run delete dataset test in Kuzu case + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_dataset_ladybug.py + + # Neo4j job - NO container (uses setup_neo4j docker action) + test-delete-dataset-neo4j: + name: Delete dataset in Neo4j graph case + runs-on: ubuntu-22.04 + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run delete dataset test in Neo4j case + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + run: uv run python ./cognee/tests/test_delete_dataset_neo4j.py + + test-graph-edges: + name: Test graph edge ingestion + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run graph edges test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_edge_ingestion.py + + run_concurrent_subprocess_access_test: + name: Concurrent Subprocess access test + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 3s + --health-retries 5 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres redis" + + - name: Run Concurrent subprocess access test (Kuzu/Lancedb/Postgres/Redis) + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + CACHING: true + CACHE_BACKEND: 'redis' + CACHE_HOST: ${{ inputs.ci-image != '' && 'redis' || 'localhost' }} + SHARED_KUZU_LOCK: true + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_concurrent_subprocess_access.py + + test-entity-extraction: + name: Test Entity Extraction + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Entity Extraction Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/tasks/entity_extraction/entity_extraction_test.py + + run_conversation_sessions_test_redis: + name: Conversation sessions test (Redis) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 3s + --health-retries 5 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres redis" + + - name: Run Conversation session tests (Redis) + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + CACHING: true + AUTO_FEEDBACK: true + CACHE_BACKEND: 'redis' + CACHE_HOST: ${{ inputs.ci-image != '' && 'redis' || 'localhost' }} + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_conversation_history.py + + run_conversation_sessions_test_fs: + name: Conversation sessions test (FS) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Run Conversation session tests (FS) + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + CACHING: true + AUTO_FEEDBACK: true + CACHE_BACKEND: 'fs' + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_conversation_history.py + + run_conversation_sessions_test_postgres: + name: Conversation sessions test (Postgres) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: pgvector/pgvector:pg17 + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Run Conversation session tests (Postgres) + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + CACHING: true + AUTO_FEEDBACK: true + CACHE_BACKEND: 'postgres' + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_conversation_history.py + + run_conversation_sessions_test_sqlite: + name: Conversation sessions test (SQLite) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Conversation session tests (SQLite) + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + CACHING: true + AUTO_FEEDBACK: true + CACHE_BACKEND: 'sqlite' + run: uv run python ./cognee/tests/test_conversation_history.py + + run-feedback-weights-pipeline-smoke-kuzu: + name: Feedback Weights Pipeline Smoke Test (Kuzu) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Run feedback weights pipeline smoke test (Kuzu) + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + CACHING: true + AUTO_FEEDBACK: false + CACHE_BACKEND: 'fs' + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_feedback_weights_memify_pipeline.py + + run-feedback-weights-pipeline-smoke-neo4j: + name: Feedback Weights Pipeline Smoke Test (Neo4j) + runs-on: ubuntu-22.04 + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run feedback weights pipeline smoke test (Neo4j) + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + CACHING: true + AUTO_FEEDBACK: false + CACHE_BACKEND: 'fs' + ENABLE_BACKEND_ACCESS_CONTROL: false + run: uv run python ./cognee/tests/test_feedback_weights_memify_pipeline.py + + run-feedback-weights-pipeline-smoke-kuzu-redis: + name: Feedback Weights Pipeline Smoke Test (Kuzu + Redis) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 3s + --health-retries 5 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres redis" + + - name: Run feedback weights pipeline smoke test (Kuzu + Redis) + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + CACHING: true + AUTO_FEEDBACK: false + CACHE_BACKEND: 'redis' + CACHE_HOST: ${{ inputs.ci-image != '' && 'redis' || 'localhost' }} + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_feedback_weights_memify_pipeline.py + + run-feedback-weights-pipeline-smoke-neo4j-redis: + name: Feedback Weights Pipeline Smoke Test (Neo4j + Redis) + runs-on: ubuntu-22.04 + services: + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 3s + --health-retries 5 + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "redis" + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run feedback weights pipeline smoke test (Neo4j + Redis) + env: + ENV: dev + ENABLE_BACKEND_ACCESS_CONTROL: false + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + CACHING: true + AUTO_FEEDBACK: false + CACHE_BACKEND: 'redis' + CACHE_HOST: 'localhost' + run: uv run python ./cognee/tests/test_feedback_weights_memify_pipeline.py + + run-pipeline-cache-test: + name: Test Pipeline Caching + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Pipeline Cache Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_pipeline_cache.py + + run-graph-model-from-schema-test: + name: Test Graph Model Generation From Schema + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Graph Model Generation From Schema Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + # Single-user E2E flow; calls visualize_graph() with no dataset. + ENABLE_BACKEND_ACCESS_CONTROL: false + run: uv run python ./cognee/tests/test_graph_model_from_schema.py + + run-custom-model-test: + name: Test Custom Model Cognify + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Custom Model Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + # Single-user E2E flow; calls visualize_graph() with no dataset. + ENABLE_BACKEND_ACCESS_CONTROL: false + run: uv run python ./cognee/tests/test_custom_model.py + + run-dataset-queue-test: + name: Test Dataset Queues Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Test Dataset Queues Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DATASET_QUEUE_ENABLED: "True" + run: uv run python ./cognee/tests/e2e/dataset_queue/test_queue_serialization_e2e.py + + run-dataset-queue-buildup-test: + name: Test Dataset Queue Buildup Cognee + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Test Dataset Queue Buildup Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DATASET_QUEUE_ENABLED: "True" + DATABASE_MAX_LRU_CACHE_SIZE: "1" + run: uv run pytest cognee/tests/e2e/dataset_queue/test_dataset_queue_buildup.py + + run_usage_logger_test: + name: Usage logger test (API/MCP) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 3s + --health-retries 5 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "redis" + + - name: Install cognee-mcp and override with local cognee + shell: bash + run: | + # 1. Install MCP (this will pull cognee from PyPI as a dependency) + uv pip install -e ./cognee-mcp + + # 2. Force-install the local root to overwrite the PyPI version + # This replaces the PyPI 'cognee' with your current branch code + uv pip install --force-reinstall -e . + + - name: Run api/tool usage logger + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + USAGE_LOGGING: true + CACHE_BACKEND: 'redis' + CACHE_HOST: ${{ inputs.ci-image != '' && 'redis' || 'localhost' }} + run: | + uv run pytest cognee/tests/test_usage_logger_e2e.py -v --log-level=INFO || { + echo "Usage logger test failed; retrying failed tests once." + uv run pytest cognee/tests/test_usage_logger_e2e.py -v --log-level=INFO --lf + } + + run_conditional_auth_test: + name: Conditional Authentication Test + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Conditional Authentication Test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + ENABLE_BACKEND_ACCESS_CONTROL: "false" + run: uv run python ./cognee/tests/api/test_conditional_authentication_endpoints.py + + run_backend_auth_tests: + name: Backend Authentication Tests + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Backend Authentication Tests + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/api/test_backend_auth.py + + run_agent_endpoint_tests: + name: Agent Endpoint Tests + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Agent Endpoint Tests + env: + ENV: 'dev' + run: uv run pytest cognee/tests/api/test_agent_endpoints.py -v + + run_agent_mode_tests: + name: Agent Mode Tests + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Agent Mode Tests + env: + ENV: 'dev' + run: uv run pytest cognee/tests/api/test_agent_mode.py -v + + run_agent_e2e_tests: + name: Agent E2E Tests + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Agent E2E Tests + env: + ENV: 'dev' + run: uv run pytest cognee/tests/api/test_agents_e2e.py -v + + run_agent_registry_tests: + name: Agent Registry Tests + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Agent Registry Tests + env: + ENV: 'dev' + run: uv run pytest cognee/tests/unit/modules/agents/test_agent_registry.py -v + + backwards-compatibility-sqlite: + name: Backwards compatibility — SQLite + LanceDB + Kuzu + runs-on: ubuntu-22.04 + + env: + COGNEE_COMPATIBILITY_TEST_VERSION: v0.5.6 + + ENV: dev + LLM_PROVIDER: ${{ secrets.LLM_PROVIDER }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_PROVIDER: ${{ secrets.EMBEDDING_PROVIDER }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + EMBEDDING_DIMENSIONS: 300 + steps: + - name: Check out current branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + + # Save test scripts — they won't exist on the legacy version tag + - name: Save compatibility test scripts + run: cp -r cognee/tests/backwards_compatibility /tmp/compat_scripts + + # ── Phase 1: legacy version ──────────────────────────────────────────── + - name: Switch to cognee ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }} + run: git checkout ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }} + + - name: Install cognee ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }} dependencies + run: uv sync --extra api --extra docs --extra dev + + - name: "Phase 1 — add, cognify, search (cognee ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }})" + run: uv run python /tmp/compat_scripts/phase1_seed.py + + # ── Alembic migrations ───────────────────────────────────────────────── + - name: Switch back to current branch + run: git checkout ${{ github.sha }} + + - name: Install current branch dependencies + run: uv sync --extra api --extra docs --extra dev + + - name: Run Alembic migrations (current branch schema → legacy version databases) + working-directory: cognee + run: uv run alembic upgrade head + + # ── Phase 2: current branch ──────────────────────────────────────────── + - name: "Phase 2 — search legacy version data, then add, cognify, search (current branch)" + run: uv run python cognee/tests/backwards_compatibility/phase2_verify.py + + backwards-compatibility-postgres: + name: Backwards compatibility — PostgreSQL + PGVector + Kuzu + runs-on: ubuntu-22.04 + + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + env: + COGNEE_COMPATIBILITY_TEST_VERSION: v0.5.6 + + # Relational DB — PostgreSQL + DB_PROVIDER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + DB_NAME: cognee_db + + # Vector DB — PGVector (same postgres instance) + VECTOR_DB_PROVIDER: pgvector + VECTOR_DB_HOST: localhost + VECTOR_DB_PORT: 5432 + VECTOR_DB_USERNAME: cognee + VECTOR_DB_PASSWORD: cognee + VECTOR_DATASET_DATABASE_HANDLER: "pgvector" + + ENV: dev + LLM_PROVIDER: ${{ secrets.LLM_PROVIDER }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_PROVIDER: ${{ secrets.EMBEDDING_PROVIDER }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + EMBEDDING_DIMENSIONS: 300 + + steps: + - name: Check out current branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + + # Save test scripts — they won't exist on the legacy version tag + - name: Save compatibility test scripts + run: cp -r cognee/tests/backwards_compatibility /tmp/compat_scripts + + # ── Phase 1: legacy version ──────────────────────────────────────────── + - name: Switch to cognee ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }} + run: git checkout ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }} + + - name: Install cognee ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }} dependencies (with postgres extra) + run: uv sync --extra api --extra docs --extra dev --extra postgres + + - name: "Phase 1 — add, cognify, search (cognee ${{ env.COGNEE_COMPATIBILITY_TEST_VERSION }} / postgres)" + run: uv run python /tmp/compat_scripts/phase1_seed.py + + # ── Alembic migrations ───────────────────────────────────────────────── + - name: Switch back to current branch + run: git checkout ${{ github.sha }} + + - name: Install current branch dependencies (with postgres extra) + run: uv sync --extra api --extra docs --extra dev --extra postgres + + - name: Run Alembic migrations (current branch schema → legacy version databases) + working-directory: cognee + run: uv run alembic upgrade head + + # ── Phase 2: current branch ──────────────────────────────────────────── + - name: "Phase 2 — search legacy version data, then add, cognify, search (current branch)" + run: uv run python cognee/tests/backwards_compatibility/phase2_verify.py + + test-adapter-caching: + name: Test Vector and Graph Adapter Caching + uses: ./.github/workflows/adapter_caching_tests.yml + with: + python-version: ${{ inputs.python-version }} + ci-image: ${{ inputs.ci-image }} + secrets: inherit + + # ── Performance Analysis Tests ───────────────────────────────────────────── + + performance-test-sqlite-lancedb-ladybug: + name: Performance Test (SQLite + LanceDB + LadyBug) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Performance Analysis Test + env: + ENV: 'dev' + run: uv run pytest cognee/tests/test_performance_analysis.py -v + + performance-test-postgres-pgvector: + name: Performance Test (PostgreSQL + PGVector + PostgresGraph) + runs-on: ubuntu-latest + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Run Performance Analysis Test + env: + ENV: 'dev' + DB_PROVIDER: 'postgres' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + DB_NAME: cognee_db + VECTOR_DB_PROVIDER: 'pgvector' + VECTOR_DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + VECTOR_DB_PORT: 5432 + VECTOR_DB_USERNAME: cognee + VECTOR_DB_PASSWORD: cognee + VECTOR_DATASET_DATABASE_HANDLER: 'pgvector' + GRAPH_DATABASE_PROVIDER: 'postgres' + GRAPH_DATABASE_USERNAME: cognee + GRAPH_DATABASE_PASSWORD: cognee + GRAPH_DATABASE_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + GRAPH_DATABASE_PORT: 5432 + GRAPH_DATABASE_NAME: "cognee_db" + run: uv run pytest cognee/tests/test_performance_analysis.py -v + + performance-test-neo4j: + name: Performance Test (SQLite + Neo4j + LanceDB) + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run Performance Analysis Test + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: false + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + run: uv run pytest cognee/tests/test_performance_analysis.py -v + + test-concurrent-cognify: + name: Concurrent cognify on one dataset end-to-end (SQLite + LanceDB) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run concurrent cognify e2e test + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: "True" + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run pytest cognee/tests/test_concurrent_cognify_e2e.py -v --log-level=INFO diff --git a/.github/workflows/examples_tests.yml b/.github/workflows/examples_tests.yml new file mode 100644 index 0000000..dbc1562 --- /dev/null +++ b/.github/workflows/examples_tests.yml @@ -0,0 +1,384 @@ +name: Reusable Examples Tests + +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + ci-image: + required: false + type: string + default: '' + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + test-multimedia-example: + name: Run Multimedia Example + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Multimedia Example + env: + ENV: 'dev' + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: uv run python ./examples/demos/multimedia_processing/multimedia_audio_image_processing_example.py + + test-eval-example: + name: Run Eval Example + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "deepeval" + + - name: Run Evaluation Framework Example + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/eval_framework/run_eval.py + + test-descriptive-metrics: + name: Run Descriptive Metrics Example + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run Descriptive Graph Metrics Example + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + run: uv run python ./cognee/tests/tasks/descriptive_metrics/neo4j_metrics_test.py + + test-temporal-example: + name: Run Temporal Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Temporal Example + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./examples/demos/temporal_awareness_example/temporal_awareness_example.py + + test-ontology-example: + name: Run Ontology Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Ontology Demo Example + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./examples/demos/ontology_reference_vocabulary/ontology_as_reference_vocabulary_example.py + + test-feedback-score-shifting-example: + name: Run Feedback Score Shifting Example + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Feedback Score Shifting Example + env: + ENV: 'dev' + CACHING: 'true' + CACHE_BACKEND: 'fs' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./examples/demos/feedback_score_shifting_example.py + + test-agentic-reasoning: + name: Run Agentic Reasoning Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Agentic Reasoning Example + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./examples/custom_pipelines/agentic_reasoning_procurement_example.py + + test-memify: + name: Run Memify Example + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Memify Tests + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./examples/custom_pipelines/memify_coding_agent_rule_extraction_example.py + + test-custom-pipeline: + name: Run Custom Pipeline Example + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Custom Pipeline Example + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./examples/custom_pipelines/custom_cognify_pipeline_example.py + + test-permissions-example: + name: Run Permissions Example + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Permissions Example + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./examples/configurations/permissions_example/user_permissions_and_access_control_example.py + + test-s3-permissions-example: # Make sure permission and multi-user mode work with S3 file system + name: Run Permissions Example + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: # Using postgres to avoid storing and using SQLite from S3 + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres aws" + + - name: Run S3 Permissions Example + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + STORAGE_BACKEND: 's3' + AWS_REGION: eu-west-1 + AWS_ENDPOINT_URL: https://s3-eu-west-1.amazonaws.com + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + STORAGE_BUCKET_NAME: github-runner-cognee-tests + DATA_ROOT_DIRECTORY: "s3://github-runner-cognee-tests/cognee/data" + SYSTEM_ROOT_DIRECTORY: "s3://github-runner-cognee-tests/cognee/system" + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./examples/configurations/permissions_example/user_permissions_and_access_control_example.py + + # Docling test - NO container (runs on macOS, container only works on Linux) + test_docling_add: + name: Run Add with Docling Test + runs-on: macos-15 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: 'docling' + + - name: Run Docling Test + env: + ENV: 'dev' + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: | + uv sync --extra docling + uv run python ./cognee/tests/test_add_docling_document.py diff --git a/.github/workflows/graph_db_tests.yml b/.github/workflows/graph_db_tests.yml new file mode 100644 index 0000000..0faa77d --- /dev/null +++ b/.github/workflows/graph_db_tests.yml @@ -0,0 +1,225 @@ +name: Reusable Graph DB Tests + +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + databases: + required: false + type: string + default: "all" + description: "Which vector databases to test (comma-separated list or 'all')" + python-version: + required: false + type: string + default: "3.11.x" + description: "Python version to use for testing" + ci-image: + required: false + type: string + default: '' + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + run-kuzu-tests: + name: Kuzu Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'kuzu') }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Kuzu Tests + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_kuzu.py + + - name: Run Weighted Edges Tests with Kuzu + env: + ENV: 'dev' + GRAPH_DATABASE_PROVIDER: "kuzu" + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run pytest cognee/tests/unit/interfaces/graph/test_weighted_edges.py -v + + run-postgres-tests: + name: Postgres Graph Tests + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'postgres') }} + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: postgres + + - name: Run Postgres Adapter Unit Tests + env: + DB_PROVIDER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + DB_NAME: cognee_db + run: uv run pytest cognee/tests/e2e/postgres/test_postgres_adapter.py -v + + - name: Run Postgres Graph E2E Test + env: + ENV: 'dev' + GRAPH_DATABASE_PROVIDER: "postgres" + GRAPH_DATABASE_URL: "postgresql+asyncpg://cognee:cognee@localhost:5432/cognee_db" + DB_PROVIDER: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + DB_NAME: cognee_db + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/e2e/postgres/test_graphdb_postgres.py + +# - name: Run Postgres Hybrid Adapter Tests +# env: +# ENV: 'dev' +# DB_PROVIDER: postgres +# DB_HOST: localhost +# DB_PORT: 5432 +# DB_USERNAME: cognee +# DB_PASSWORD: cognee +# DB_NAME: cognee_db +# EMBEDDING_DIMENSIONS: 300 +# EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} +# EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} +# run: uv run pytest cognee/tests/e2e/postgres/test_postgres_hybrid_adapter.py -v +# +# - name: Run Postgres Hybrid E2E Test +# env: +# ENV: 'dev' +# USE_UNIFIED_PROVIDER: "pghybrid" +# DB_PROVIDER: postgres +# DB_HOST: localhost +# DB_PORT: 5432 +# DB_USERNAME: cognee +# DB_PASSWORD: cognee +# DB_NAME: cognee_db +# ENABLE_BACKEND_ACCESS_CONTROL: 'false' +# LLM_MODEL: ${{ secrets.LLM_MODEL }} +# LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} +# LLM_API_KEY: ${{ secrets.LLM_API_KEY }} +# LLM_ARGS: ${{ secrets.LLM_ARGS }} +# LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} +# EMBEDDING_DIMENSIONS: 300 +# EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} +# EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} +# run: uv run python ./cognee/tests/e2e/postgres/test_pghybrid.py + + run-neo4j-tests: + name: Neo4j Tests + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'neo4j') }} + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Run default Neo4j + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + run: uv run python ./cognee/tests/test_neo4j.py + + - name: Run Weighted Edges Tests with Neo4j + env: + ENV: 'dev' + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ secrets.NEO4J_API_URL }} + GRAPH_DATABASE_PASSWORD: ${{ secrets.NEO4J_API_KEY }} + GRAPH_DATABASE_USERNAME: "neo4j" + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run pytest cognee/tests/unit/interfaces/graph/test_weighted_edges.py -v diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml new file mode 100644 index 0000000..5a8cb68 --- /dev/null +++ b/.github/workflows/integration_tests.yml @@ -0,0 +1,192 @@ +name: Reusable Integration Tests + +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + ci-image: + required: false + type: string + default: '' + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + integration-retrieval: + name: Integration - Retrieval (${{ matrix.split-group }}/4) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + split-group: [1, 2, 3, 4] + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "scraping fastembed" + + - name: Run Retrieval Tests + run: uv run pytest cognee/tests/integration/retrieval/ --splits 4 --group ${{ matrix.split-group }} + + integration-documents-tasks: + name: Integration - Documents & Tasks (${{ matrix.split-group }}/2) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + split-group: [1, 2] + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "scraping" + + - name: Run Document & Task Tests + run: uv run pytest cognee/tests/integration/documents/ cognee/tests/integration/tasks/ --splits 2 --group ${{ matrix.split-group }} + + integration-infra: + name: Integration - Infrastructure (${{ matrix.split-group }}/3) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + split-group: [1, 2, 3] + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + # fastembed so the embedding tokenizer delegation tests can import + # FastembedEmbeddingEngine. + extra-dependencies: "scraping fastembed" + + - name: Run Infrastructure Tests + run: uv run pytest cognee/tests/integration/infrastructure/ --splits 3 --group ${{ matrix.split-group }} + + integration-web: + name: Integration - Web + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "scraping" + + - name: Run Web Integration Tests + run: uv run pytest cognee/tests/integration/web_url_crawler/ + + integration-other: + name: Integration - Deletion & Misc + runs-on: ubuntu-22.04 + services: + redis: + image: redis:7 + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 3s + --health-retries 5 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + CACHE_HOST: redis + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "scraping redis" + + - name: Run Deletion & Misc Tests + run: uv run pytest cognee/tests/integration/test_delete_edge_cases.py cognee/tests/integration/shared/ diff --git a/.github/workflows/label-core-team.yml b/.github/workflows/label-core-team.yml new file mode 100644 index 0000000..d792e81 --- /dev/null +++ b/.github/workflows/label-core-team.yml @@ -0,0 +1,74 @@ +name: Label PRs from core team + +on: + pull_request_target: + types: [opened, reopened, synchronize, ready_for_review, edited] + +permissions: + contents: read + issues: write + +jobs: + label-core-team: + if: ${{ !github.event.pull_request.draft }} + runs-on: ubuntu-latest + steps: + - name: Check out base repository + uses: actions/checkout@v6 + with: + repository: ${{ github.repository }} + ref: ${{ github.event.pull_request.base.ref }} + + - name: Determine if PR author is a core team member + id: check_core + shell: bash + run: | + AUTHOR="${{ github.event.pull_request.user.login }}" + LIST_FILE=".github/core-team.txt" + + if [ ! -f "$LIST_FILE" ]; then + echo "core=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Normalize author to lowercase and strip leading '@' + AUTHOR_NORM="$(echo "$AUTHOR" | tr '[:upper:]' '[:lower:]' | sed 's/^@//')" + + # Compare against normalized list values (ignore comments/blank lines) + if awk -v author="$AUTHOR_NORM" ' + BEGIN { found=0 } + { + line=$0 + sub(/^[ \t]+|[ \t]+$/, "", line) + if (line ~ /^#/ || line == "") next + sub(/^@/, "", line) + line=tolower(line) + if (line == author) { found=1; exit } + } + END { exit(found ? 0 : 1) } + ' "$LIST_FILE"; then + echo "core=true" >> "$GITHUB_OUTPUT" + else + echo "core=false" >> "$GITHUB_OUTPUT" + fi + + - name: Add core-team label + if: steps.check_core.outputs.core == 'true' + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const label = 'core-team'; + const { owner, repo } = context.repo; + const prNumber = context.payload.pull_request.number; + try { + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: prNumber, + labels: [label], + }); + core.info(`Label '${label}' added to PR #${prNumber}`); + } catch (error) { + core.warning(`Failed to add label: ${error.message}`); + } diff --git a/.github/workflows/load_tests.yml b/.github/workflows/load_tests.yml new file mode 100644 index 0000000..15a99b0 --- /dev/null +++ b/.github/workflows/load_tests.yml @@ -0,0 +1,73 @@ +name: Load tests + +permissions: + contents: read + packages: read + +on: + workflow_dispatch: + workflow_call: + inputs: + ci-image: + required: false + type: string + default: '' + secrets: + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + OPENAI_API_KEY: + required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + +jobs: + test-load: + name: Test Load + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + timeout-minutes: 60 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "aws" + + - name: Verify File Descriptor Limit + run: ulimit -n + + - name: Run Load Test + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: True + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + STORAGE_BACKEND: s3 + AWS_REGION: eu-west-1 + AWS_ENDPOINT_URL: https://s3-eu-west-1.amazonaws.com + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + run: uv run python ./cognee/tests/test_load.py diff --git a/.github/workflows/mirror-images.yml b/.github/workflows/mirror-images.yml new file mode 100644 index 0000000..0145519 --- /dev/null +++ b/.github/workflows/mirror-images.yml @@ -0,0 +1,54 @@ +name: Mirror external images to GHCR + +# Copies the pinned third-party images CI depends on (pgvector) into GHCR, so the +# PR / test hot path never pulls from Docker Hub — which is rate-limited and flaky +# from GitHub Actions (intermittent `registry-1.docker.io ... timeout` on service +# container setup). The test workflows reference ghcr.io//pgvector:pg17. +# +# Runs weekly for freshness and on-demand. IMPORTANT: run this once (Actions -> +# "Mirror external images to GHCR" -> Run workflow) BEFORE the GHCR references go +# live, so the image exists. Ensure the resulting GHCR package is accessible to +# this repo (public, or repo-linked) so service pulls with GITHUB_TOKEN succeed. + +on: + schedule: + - cron: "0 6 * * 1" # Mondays 06:00 UTC + workflow_dispatch: {} + +permissions: + contents: read + packages: write + +jobs: + mirror: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: + - pgvector/pgvector:pg17 + steps: + - name: Log in to Docker Hub (authenticated source pulls) + uses: docker/login-action@v3 + continue-on-error: true # best-effort: fall back to anonymous if unset + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Mirror ${{ matrix.image }} -> GHCR + run: | + set -euo pipefail + SRC="${{ matrix.image }}" + DST="ghcr.io/${{ github.repository_owner }}/${SRC##*/}" + echo "Mirroring $SRC -> $DST" + docker pull "$SRC" + docker tag "$SRC" "$DST" + docker push "$DST" + echo "Done: $DST" diff --git a/.github/workflows/nightly_tests.yml b/.github/workflows/nightly_tests.yml new file mode 100644 index 0000000..54822a5 --- /dev/null +++ b/.github/workflows/nightly_tests.yml @@ -0,0 +1,246 @@ +name: Nightly Tests +permissions: + contents: read + packages: write + +on: + schedule: + - cron: '0 3 * * *' + workflow_dispatch: + # Note: Run Nightly CI on change to any of the following files. + pull_request: + paths: + - '.github/workflows/nightly_tests.yml' + - '.github/workflows/performance_report.yml' + + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + +jobs: + ollama-tests: + name: Ollama Tests + uses: ./.github/workflows/test_ollama.yml + secrets: inherit + + llamacpp-tests: + name: Llama-cpp Tests + uses: ./.github/workflows/test_llamacpp.yml + secrets: inherit + + # ══ Performance: each caller runs BOTH file_based and postgres backends ════ + perf-small-llm: + name: Performance — 50 Small Documents (real LLM) + uses: ./.github/workflows/performance_report.yml + with: + mode: llm + label: 50_small_documents + runs: '3' + num_memories: '50' + memories_key: nightly_ci_artifacts/performance_test_artifacts/memories.json + secrets: inherit + + perf-small-mock: + name: Performance — 50 Small Documents (mock LLM) + uses: ./.github/workflows/performance_report.yml + with: + mode: mock_llm + label: 50_small_documents + runs: '3' + num_memories: '50' + memories_key: nightly_ci_artifacts/performance_test_artifacts/memories.json + mock_memories_key: nightly_ci_artifacts/performance_test_artifacts/mock_memories.json + secrets: inherit + + perf-wap-llm: + name: Performance — War and Peace (real LLM) + uses: ./.github/workflows/performance_report.yml + with: + mode: llm + label: war_and_peace + runs: '3' + memories_key: nightly_ci_artifacts/performance_test_artifacts/war_and_peace.json + secrets: inherit + + perf-wap-mock: + name: Performance — War and Peace (mock LLM) + uses: ./.github/workflows/performance_report.yml + with: + mode: mock_llm + label: war_and_peace + runs: '3' + memories_key: nightly_ci_artifacts/performance_test_artifacts/war_and_peace.json + mock_memories_key: nightly_ci_artifacts/performance_test_artifacts/mock_war_and_peace.json + secrets: inherit + + notify: + name: Test Completion Status + needs: [ + ollama-tests, + llamacpp-tests, + perf-small-llm, + perf-small-mock, + perf-wap-llm, + perf-wap-mock, + ] + runs-on: ubuntu-latest + if: ${{ !cancelled() }} + steps: + - name: Determine status + id: status + run: | + if [[ "${{ needs.ollama-tests.result }}" == "success" && + "${{ needs.llamacpp-tests.result }}" == "success" && + "${{ needs.perf-small-llm.result }}" == "success" && + "${{ needs.perf-small-mock.result }}" == "success" && + "${{ needs.perf-wap-llm.result }}" == "success" && + "${{ needs.perf-wap-mock.result }}" == "success" ]]; then + echo "passed=true" >> "$GITHUB_OUTPUT" + echo "emoji=✅" >> "$GITHUB_OUTPUT" + echo "summary=All nightly test suites completed successfully!" >> "$GITHUB_OUTPUT" + else + echo "passed=false" >> "$GITHUB_OUTPUT" + echo "emoji=❌" >> "$GITHUB_OUTPUT" + echo "summary=One or more nightly test suites failed." >> "$GITHUB_OUTPUT" + fi + echo "ran_at=$(date -u '+%Y-%m-%d %H:%M UTC')" >> "$GITHUB_OUTPUT" + + - name: Presign performance report links + id: presign + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + AWS_DEFAULT_REGION: eu-west-1 + BUCKET: github-runner-cognee-tests + # " " per line. The key already encodes the + # backend (file_based/ or postgres/), so presigning is fully generic. + REPORT_KEYS: | + url_file_small_llm ${{ needs.perf-small-llm.outputs.file_based_html_key }} + url_file_small_mock ${{ needs.perf-small-mock.outputs.file_based_html_key }} + url_file_wap_llm ${{ needs.perf-wap-llm.outputs.file_based_html_key }} + url_file_wap_mock ${{ needs.perf-wap-mock.outputs.file_based_html_key }} + url_pg_small_llm ${{ needs.perf-small-llm.outputs.postgres_html_key }} + url_pg_small_mock ${{ needs.perf-small-mock.outputs.postgres_html_key }} + url_pg_wap_llm ${{ needs.perf-wap-llm.outputs.postgres_html_key }} + url_pg_wap_mock ${{ needs.perf-wap-mock.outputs.postgres_html_key }} + run: | + set -euo pipefail + # 7 days = 604800s is the maximum lifetime for an IAM-user presigned URL. + # Skip empty keys (a failed perf job produces no report). + while read -r name key; do + [ -n "${key:-}" ] || continue + echo "$name=$(aws s3 presign "s3://$BUCKET/$key" --expires-in 604800)" >> "$GITHUB_OUTPUT" + done <<< "$REPORT_KEYS" + + - name: Post status to Slack + uses: slackapi/slack-github-action@v2.1.0 + with: + method: chat.postMessage + token: ${{ secrets.SLACK_NIGHTLY_BOT_TOKEN }} + payload: | + channel: ${{ secrets.SLACK_NIGHTLY_CHANNEL_ID }} + text: "${{ steps.status.outputs.emoji }} Nightly Tests (${{ steps.status.outputs.ran_at }}): ${{ steps.status.outputs.summary }}" + blocks: + - type: "section" + text: + type: "mrkdwn" + text: | + ${{ steps.status.outputs.emoji }} *Nightly Tests* — ${{ steps.status.outputs.summary }} + *Ran at:* `${{ steps.status.outputs.ran_at }}` + *Ollama:* `${{ needs.ollama-tests.result }}` • *Llama-cpp:* `${{ needs.llamacpp-tests.result }}` + - type: "section" + text: + type: "mrkdwn" + text: | + *📊 File Based — 50 Small Documents — Real LLM* (`${{ needs.perf-small-llm.result }}`) • runs `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.file_based_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_file_small_llm }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + *📊 File Based — 50 Small Documents — Mock LLM* (`${{ needs.perf-small-mock.result }}`) • runs `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.file_based_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_file_small_mock }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + *📚 File Based — War and Peace — Real LLM* (`${{ needs.perf-wap-llm.result }}`) • runs `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.file_based_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_file_wap_llm }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + *📚 File Based — War and Peace — Mock LLM* (`${{ needs.perf-wap-mock.result }}`) • runs `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.file_based_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_file_wap_mock }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + *📊 Full Postgres — 50 Small Documents — Real LLM* (`${{ needs.perf-small-llm.result }}`) • runs `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-llm.outputs.postgres_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_pg_small_llm }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + *📊 Full Postgres — 50 Small Documents — Mock LLM* (`${{ needs.perf-small-mock.result }}`) • runs `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-small-mock.outputs.postgres_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_pg_small_mock }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + *📚 Full Postgres — War and Peace — Real LLM* (`${{ needs.perf-wap-llm.result }}`) • runs `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-llm.outputs.postgres_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_pg_wap_llm }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + *📚 Full Postgres — War and Peace — Mock LLM* (`${{ needs.perf-wap-mock.result }}`) • runs `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').success }}` + add p50 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').add.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').add.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').add.p99 }}s` + cognify p50 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').cognify.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').cognify.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').cognify.p99 }}s` + search p50 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').search.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').search.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').search.p99 }}s` + total p50 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').total.p50 }}s` • p90 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').total.p90 }}s` • p99 `${{ fromJSON(needs.perf-wap-mock.outputs.postgres_metrics || '{}').total.p99 }}s` + <${{ steps.presign.outputs.url_pg_wap_mock }}|HTML report> + - type: "section" + text: + type: "mrkdwn" + text: | + <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run> + + - name: Fail if any suite failed + if: ${{ steps.status.outputs.passed != 'true' }} + run: | + echo "One or more nightly test suites failed." + exit 1 diff --git a/.github/workflows/notebooks_tests.yml b/.github/workflows/notebooks_tests.yml new file mode 100644 index 0000000..657ba00 --- /dev/null +++ b/.github/workflows/notebooks_tests.yml @@ -0,0 +1,28 @@ +name: Reusable Notebook Tests + +on: + workflow_call: + inputs: + ci-image: + required: false + type: string + default: '' + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: +# run-main-notebook: +# name: Main Notebook Test +# uses: ./.github/workflows/reusable_notebook.yml +# with: +# notebook-location: notebooks/cognee_demo.ipynb +# secrets: inherit + + run-cognee-multimedia: + name: Cognee Multimedia Notebook + uses: ./.github/workflows/reusable_notebook.yml + with: + notebook-location: notebooks/cognee_multimedia_demo.ipynb + ci-image: ${{ inputs.ci-image }} + secrets: inherit diff --git a/.github/workflows/performance_report.yml b/.github/workflows/performance_report.yml new file mode 100644 index 0000000..19d6bfd --- /dev/null +++ b/.github/workflows/performance_report.yml @@ -0,0 +1,298 @@ +name: performance report + +# Reusable workflow: runs the percentile performance report once for a given +# dataset + mode, uploads the JSON + HTML artifacts to S3, and exposes the +# headline metrics + HTML object key as outputs for the caller (the Slack bot in +# nightly_tests.yml). +# +# Both backends run on every call, as two separate jobs: +# - file_based : cognee defaults (SQLite + Kuzu + LanceDB), no services. +# - postgres : full Postgres (relational + PGVector + Postgres graph) via a +# `services: postgres` container (mirrors e2e_tests.yml). +# Each job exposes its own metrics + html_key outputs (file_based_* / postgres_*). +on: + workflow_call: + inputs: + mode: + description: "Run mode: 'mock_llm' (no API calls) or 'llm' (real LLM)." + required: true + type: string + label: + description: "Dataset label, used in the S3 output path and display name." + required: true + type: string + runs: + description: "Number of sequential benchmark runs." + required: false + type: string + default: '10' + num_memories: + description: "If set, forwarded as --num-memories (limit input documents)." + required: false + type: string + default: '' + memories_key: + description: "S3 object key (under the bucket) downloaded and used as --memories." + required: true + type: string + mock_memories_key: + description: "If set, S3 object key downloaded and used as --mock-memories." + required: false + type: string + default: '' + outputs: + file_based_metrics: + description: "File-based run: success + add/cognify/search/total p50/p90/p99." + value: ${{ jobs.file_based.outputs.metrics }} + file_based_html_key: + description: "File-based run: S3 object key of the HTML report." + value: ${{ jobs.file_based.outputs.html_key }} + postgres_metrics: + description: "Postgres run: success + add/cognify/search/total p50/p90/p99." + value: ${{ jobs.postgres.outputs.metrics }} + postgres_html_key: + description: "Postgres run: S3 object key of the HTML report." + value: ${{ jobs.postgres.outputs.html_key }} + +env: + ENV: 'dev' + COGNEE_SKIP_CONNECTION_TEST: 'true' + RUNTIME__LOG_LEVEL: ERROR + BUCKET: github-runner-cognee-tests + +jobs: + # ── File-based backend: SQLite + Kuzu + LanceDB (cognee defaults) ──────────── + file_based: + name: file_based — ${{ inputs.label }} (${{ inputs.mode }}) + runs-on: ubuntu-22.04 + outputs: + metrics: ${{ steps.parse.outputs.metrics }} + html_key: ${{ steps.upload.outputs.html_key }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Download dataset from S3 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + AWS_DEFAULT_REGION: eu-west-1 + MEMORIES_KEY: ${{ inputs.memories_key }} + MOCK_MEMORIES_KEY: ${{ inputs.mock_memories_key }} + run: | + set -euo pipefail + mkdir -p performance_datasets + aws s3 cp "s3://$BUCKET/$MEMORIES_KEY" performance_datasets/memories.json + echo "MEMORIES_FILE=$PWD/performance_datasets/memories.json" >> "$GITHUB_ENV" + if [ -n "$MOCK_MEMORIES_KEY" ]; then + aws s3 cp "s3://$BUCKET/$MOCK_MEMORIES_KEY" performance_datasets/mock_memories.json + echo "MOCK_MEMORIES_FILE=$PWD/performance_datasets/mock_memories.json" >> "$GITHUB_ENV" + fi + + - name: Run performance report + id: run + env: + PYTHONFAULTHANDLER: 1 + # LLM + embedding config via the standard CI secrets (see e2e_tests.yml). + # Only exercised for mode == 'llm'; the mock run hits no external APIs. + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: | + set -euo pipefail + TS="$(date -u '+%Y-%m-%d_%H-%M-%SZ')" + JSON_PATH="performance_results/file_based/${{ inputs.label }}/${{ inputs.mode }}_${TS}.json" + HTML_PATH="performance_results/file_based/${{ inputs.label }}/${{ inputs.mode }}_${TS}.html" + mkdir -p "$(dirname "$JSON_PATH")" + echo "JSON_PATH=$JSON_PATH" >> "$GITHUB_ENV" + echo "HTML_PATH=$HTML_PATH" >> "$GITHUB_ENV" + + ARGS=(--runs "${{ inputs.runs }}" --memories "$MEMORIES_FILE") + if [ -n "${MOCK_MEMORIES_FILE:-}" ]; then + ARGS+=(--mock-memories "$MOCK_MEMORIES_FILE") + fi + if [ "${{ inputs.mode }}" = "mock_llm" ]; then + ARGS+=(--mock-llm) + fi + if [ -n "${{ inputs.num_memories }}" ]; then + ARGS+=(--num-memories "${{ inputs.num_memories }}") + fi + + uv run python cognee/tests/performance/statistics_percentile_report.py \ + "${ARGS[@]}" \ + --output "$JSON_PATH" \ + --html "$HTML_PATH" + + - name: Upload reports to S3 + id: upload + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + AWS_DEFAULT_REGION: eu-west-1 + run: | + set -euo pipefail + aws s3 cp "$JSON_PATH" "s3://$BUCKET/$JSON_PATH" --content-type application/json + aws s3 cp "$HTML_PATH" "s3://$BUCKET/$HTML_PATH" --content-type text/html + # Presigning is done by the caller (the Slack job), NOT here: a presigned URL + # embeds the AWS access key id, and GitHub scrubs registered secrets from + # reusable-workflow outputs — which would blank the link. Pass only the + # (non-secret) object key across the boundary. + echo "html_key=$HTML_PATH" >> "$GITHUB_OUTPUT" + + - name: Parse headline metrics + id: parse + run: | + set -euo pipefail + METRICS="$(jq -c '{ + success: "\(.succeeded)/\(.num_runs)", + add: {p50: .stats.add_time_s.p50, p90: .stats.add_time_s.p90, p99: .stats.add_time_s.p99}, + cognify: {p50: .stats.cognify_time_s.p50, p90: .stats.cognify_time_s.p90, p99: .stats.cognify_time_s.p99}, + search: {p50: .stats.search_time.p50, p90: .stats.search_time.p90, p99: .stats.search_time.p99}, + total: {p50: .stats.total_ingest_time_s.p50, p90: .stats.total_ingest_time_s.p90, p99: .stats.total_ingest_time_s.p99} + }' "$JSON_PATH")" + echo "metrics=$METRICS" >> "$GITHUB_OUTPUT" + + # ── Postgres backend: Postgres relational + PGVector + Postgres graph ──────── + postgres: + name: postgres — ${{ inputs.label }} (${{ inputs.mode }}) + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + outputs: + metrics: ${{ steps.parse.outputs.metrics }} + html_key: ${{ steps.upload.outputs.html_key }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Download dataset from S3 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + AWS_DEFAULT_REGION: eu-west-1 + MEMORIES_KEY: ${{ inputs.memories_key }} + MOCK_MEMORIES_KEY: ${{ inputs.mock_memories_key }} + run: | + set -euo pipefail + mkdir -p performance_datasets + aws s3 cp "s3://$BUCKET/$MEMORIES_KEY" performance_datasets/memories.json + echo "MEMORIES_FILE=$PWD/performance_datasets/memories.json" >> "$GITHUB_ENV" + if [ -n "$MOCK_MEMORIES_KEY" ]; then + aws s3 cp "s3://$BUCKET/$MOCK_MEMORIES_KEY" performance_datasets/mock_memories.json + echo "MOCK_MEMORIES_FILE=$PWD/performance_datasets/mock_memories.json" >> "$GITHUB_ENV" + fi + + - name: Run performance report + id: run + env: + PYTHONFAULTHANDLER: 1 + # LLM + embedding config via the standard CI secrets (see e2e_tests.yml). + # Only exercised for mode == 'llm'; the mock run hits no external APIs. + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + # Full Postgres stack (creds match the postgres service above; mirrors e2e_tests.yml). + DB_PROVIDER: 'postgres' + DB_HOST: '127.0.0.1' + DB_PORT: '5432' + DB_USERNAME: cognee + DB_PASSWORD: cognee + DB_NAME: cognee_db + VECTOR_DB_PROVIDER: 'pgvector' + VECTOR_DB_HOST: '127.0.0.1' + VECTOR_DB_PORT: '5432' + VECTOR_DB_USERNAME: cognee + VECTOR_DB_PASSWORD: cognee + VECTOR_DATASET_DATABASE_HANDLER: 'pgvector' + GRAPH_DATABASE_PROVIDER: 'postgres' + GRAPH_DATABASE_HOST: '127.0.0.1' + GRAPH_DATABASE_PORT: '5432' + GRAPH_DATABASE_USERNAME: cognee + GRAPH_DATABASE_PASSWORD: cognee + GRAPH_DATABASE_NAME: cognee_db + run: | + set -euo pipefail + TS="$(date -u '+%Y-%m-%d_%H-%M-%SZ')" + JSON_PATH="performance_results/postgres/${{ inputs.label }}/${{ inputs.mode }}_${TS}.json" + HTML_PATH="performance_results/postgres/${{ inputs.label }}/${{ inputs.mode }}_${TS}.html" + mkdir -p "$(dirname "$JSON_PATH")" + echo "JSON_PATH=$JSON_PATH" >> "$GITHUB_ENV" + echo "HTML_PATH=$HTML_PATH" >> "$GITHUB_ENV" + + ARGS=(--runs "${{ inputs.runs }}" --memories "$MEMORIES_FILE") + if [ -n "${MOCK_MEMORIES_FILE:-}" ]; then + ARGS+=(--mock-memories "$MOCK_MEMORIES_FILE") + fi + if [ "${{ inputs.mode }}" = "mock_llm" ]; then + ARGS+=(--mock-llm) + fi + if [ -n "${{ inputs.num_memories }}" ]; then + ARGS+=(--num-memories "${{ inputs.num_memories }}") + fi + + uv run python cognee/tests/performance/statistics_percentile_report.py \ + "${ARGS[@]}" \ + --output "$JSON_PATH" \ + --html "$HTML_PATH" + + - name: Upload reports to S3 + id: upload + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + AWS_DEFAULT_REGION: eu-west-1 + run: | + set -euo pipefail + aws s3 cp "$JSON_PATH" "s3://$BUCKET/$JSON_PATH" --content-type application/json + aws s3 cp "$HTML_PATH" "s3://$BUCKET/$HTML_PATH" --content-type text/html + # Presigning is done by the caller (the Slack job), NOT here: a presigned URL + # embeds the AWS access key id, and GitHub scrubs registered secrets from + # reusable-workflow outputs — which would blank the link. Pass only the + # (non-secret) object key across the boundary. + echo "html_key=$HTML_PATH" >> "$GITHUB_OUTPUT" + + - name: Parse headline metrics + id: parse + run: | + set -euo pipefail + METRICS="$(jq -c '{ + success: "\(.succeeded)/\(.num_runs)", + add: {p50: .stats.add_time_s.p50, p90: .stats.add_time_s.p90, p99: .stats.add_time_s.p99}, + cognify: {p50: .stats.cognify_time_s.p50, p90: .stats.cognify_time_s.p90, p99: .stats.cognify_time_s.p99}, + search: {p50: .stats.search_time.p50, p90: .stats.search_time.p90, p99: .stats.search_time.p99}, + total: {p50: .stats.total_ingest_time_s.p50, p90: .stats.total_ingest_time_s.p90, p99: .stats.total_ingest_time_s.p99} + }' "$JSON_PATH")" + echo "metrics=$METRICS" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/pr_lint b/.github/workflows/pr_lint new file mode 100644 index 0000000..f0e7398 --- /dev/null +++ b/.github/workflows/pr_lint @@ -0,0 +1,44 @@ +## Reference: https://github.com/amannn/action-semantic-pull-request +--- +name: lint | PR + +on: + pull_request: + types: + - opened + - reopened + - edited + - synchronize + +permissions: + contents: read + +jobs: + pr_title: + permissions: + pull-requests: write + statuses: write + name: Validate & Label PR + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Configure which types are allowed (newline-delimited). + # From: https://github.com/commitizen/conventional-commit-types/blob/master/index.json + # listing all below + types: | + chore + ci + docs + feat + fix + perf + refactor + revert + test + break + - uses: release-drafter/release-drafter@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pre_test.yml b/.github/workflows/pre_test.yml new file mode 100644 index 0000000..b56c5b8 --- /dev/null +++ b/.github/workflows/pre_test.yml @@ -0,0 +1,29 @@ +on: + workflow_call: + +permissions: + contents: read + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + check-uv-lock: + name: Lockfile and Pre-commit Hooks + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Validate uv lockfile and project metadata + run: uv lock --check || { echo "'uv lock --check' failed."; echo "Run 'uv lock' and push your changes."; exit 1; } + + - name: Run pre-commit hooks + uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/relational_db_migration_tests.yml b/.github/workflows/relational_db_migration_tests.yml new file mode 100644 index 0000000..a0c9f95 --- /dev/null +++ b/.github/workflows/relational_db_migration_tests.yml @@ -0,0 +1,248 @@ +name: Relational DB Migration Tests + +on: + workflow_call: + inputs: + python-version: + required: false + type: string + default: '3.11.x' + ci-image: + required: false + type: string + default: '' + secrets: + LLM_PROVIDER: + required: true + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_PROVIDER: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + run-relational-db-migration-test-networkx: + name: NetworkX Relational DB Migration Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: test_migration_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Install specific db dependency + run: echo "Dependencies already installed in setup" + + - name: Run PostgreSQL Script to create test data (Chinook_PostgreSql.sql) + env: + PGPASSWORD: cognee + PG_HOST: ${{ inputs.ci-image != '' && 'postgres' || 'localhost' }} + run: | + # Wait until the PostgreSQL service is available + until pg_isready -h $PG_HOST -p 5432; do + echo "Waiting for postgres..." + sleep 2 + done + + # Execute the SQL script against the test_migration_db database + psql -h $PG_HOST -U cognee -d test_migration_db -f ./cognee/tests/test_data/Chinook_PostgreSql.sql + + - name: Run relational db test + env: + ENV: 'dev' + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + run: uv run python ./cognee/tests/test_relational_db_migration.py + + run-relational-db-migration-test-kuzu: + name: Kuzu Relational DB Migration Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: test_migration_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Install specific db dependency + run: echo "Dependencies already installed in setup" + + - name: Run PostgreSQL Script to create test data (Chinook_PostgreSql.sql) + env: + PGPASSWORD: cognee + PG_HOST: ${{ inputs.ci-image != '' && 'postgres' || 'localhost' }} + run: | + # Wait until the PostgreSQL service is available + until pg_isready -h $PG_HOST -p 5432; do + echo "Waiting for postgres..." + sleep 2 + done + + # Execute the SQL script against the test_migration_db database + psql -h $PG_HOST -U cognee -d test_migration_db -f ./cognee/tests/test_data/Chinook_PostgreSql.sql + + - name: Run relational db test + env: + ENV: 'dev' + GRAPH_DATABASE_PROVIDER: 'kuzu' + + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + run: uv run python ./cognee/tests/test_relational_db_migration.py + + run-relational-db-migration-test-neo4j: + name: Neo4j Relational DB Migration Test + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: test_migration_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "postgres" + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Install specific db dependency + run: echo "Dependencies already installed in setup" + + - name: Run PostgreSQL Script to create test data (Chinook_PostgreSql.sql) + env: + PGPASSWORD: cognee + run: | + # Wait until the PostgreSQL service is available + until pg_isready -h localhost -p 5432; do + echo "Waiting for postgres..." + sleep 2 + done + + # Execute the SQL script against the test_migration_db database + psql -h localhost -U cognee -d test_migration_db -f ./cognee/tests/test_data/Chinook_PostgreSql.sql + + - name: Run relational db test + env: + ENV: 'dev' + GRAPH_DATABASE_PROVIDER: "neo4j" + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_relational_db_migration.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1c40765 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,254 @@ +name: release.yml +on: + workflow_dispatch: + +jobs: + release-github: + name: Create GitHub Release from ${{ github.ref_name }} + outputs: + tag: ${{ steps.create_tag.outputs.tag }} + version: ${{ steps.create_tag.outputs.version }} + permissions: + contents: write + runs-on: ubuntu-latest + + steps: + - name: Check out ${{ github.ref_name }} + uses: actions/checkout@v6 + with: + ref: ${{ github.ref_name }} + fetch-depth: 0 # Fetch all history for comparison + + - name: Fetch main branch for comparison + if: ${{ github.ref_name == 'dev' }} + run: git fetch origin main:main + + - name: Fetch dev branch for comparison + if: ${{ github.ref_name == 'main' }} + run: git fetch origin dev:dev + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Install dependencies + run: uv sync --locked + + - name: Create and push git tag + id: create_tag + run: | + VERSION="$(uv version --short)" + TAG="v${VERSION}" + + echo "Tag to create: ${TAG}" + + git config user.name "Cognee Team" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + + git tag "${TAG}" + git push origin "${TAG}" + + - name: Generate AI-powered release notes + id: generate_notes + env: + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MODEL: ${{ secrets.LLM_MODEL != '' && secrets.LLM_MODEL || 'openai/gpt-4o-mini' }} + run: | + # Set PYTHONPATH to include project root + export PYTHONPATH="${GITHUB_WORKSPACE}:${PYTHONPATH}" + + # Generate release notes + # Compares current branch against the latest release tag automatically + uv run python tools/generate_release_notes.py \ + --version "${{ steps.create_tag.outputs.version }}" \ + --github-output + + - name: Create GitHub Release with AI-generated notes + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.create_tag.outputs.tag }} + name: ${{ steps.generate_notes.outputs.RELEASE_TITLE }} + body: ${{ steps.generate_notes.outputs.RELEASE_NOTES }} + prerelease: ${{ github.ref_name == 'dev' }} + env: + GITHUB_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }} + + release-pypi-package: + needs: release-github + name: Release PyPI Package from ${{ github.ref_name }} + permissions: + contents: read + runs-on: ubuntu-latest + + steps: + - name: Check out ${{ github.ref_name }} + uses: actions/checkout@v6 + with: + ref: ${{ github.ref_name }} + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Install dependencies + run: uv sync --locked --all-extras + + - name: Build distributions + run: uv build + + - name: Publish ${{ github.ref_name }} release to PyPI + env: + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} + run: uv publish + + release-docker-image: + needs: release-github + name: Release Docker Image from ${{ github.ref_name }} + permissions: + contents: read + runs-on: ubuntu-latest + + steps: + - name: Check out ${{ github.ref_name }} + uses: actions/checkout@v6 + with: + ref: ${{ github.ref_name }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push Dev Docker Image + if: ${{ github.ref_name == 'dev' }} + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: cognee/cognee:${{ needs.release-github.outputs.version }} + labels: | + version=${{ needs.release-github.outputs.version }} + flavour=${{ github.ref_name }} + cache-from: type=registry,ref=cognee/cognee:buildcache + cache-to: type=registry,ref=cognee/cognee:buildcache,mode=max + + - name: Build and push Main Docker Image + if: ${{ github.ref_name == 'main' }} + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: | + cognee/cognee:${{ needs.release-github.outputs.version }} + cognee/cognee:latest + labels: | + version=${{ needs.release-github.outputs.version }} + flavour=${{ github.ref_name }} + cache-from: type=registry,ref=cognee/cognee:buildcache + cache-to: type=registry,ref=cognee/cognee:buildcache,mode=max + + - name: Build and push Dev MCP Docker Image + if: ${{ github.ref_name == 'dev' }} + uses: docker/build-push-action@v6 + with: + context: . + file: cognee-mcp/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: cognee/cognee-mcp:${{ needs.release-github.outputs.version }} + labels: | + version=${{ needs.release-github.outputs.version }} + flavour=${{ github.ref_name }} + cache-from: type=registry,ref=cognee/cognee-mcp:buildcache + cache-to: type=registry,ref=cognee/cognee-mcp:buildcache,mode=max + + - name: Build and push Main MCP Docker Image + if: ${{ github.ref_name == 'main' }} + uses: docker/build-push-action@v6 + with: + context: . + file: cognee-mcp/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: | + cognee/cognee-mcp:${{ needs.release-github.outputs.version }} + cognee/cognee-mcp:latest + labels: | + version=${{ needs.release-github.outputs.version }} + flavour=${{ github.ref_name }} + cache-from: type=registry,ref=cognee/cognee-mcp:buildcache + cache-to: type=registry,ref=cognee/cognee-mcp:buildcache,mode=max + + trigger-docs-test-suite: + needs: release-pypi-package + if: ${{ github.ref_name == 'main' }} + runs-on: ubuntu-22.04 + steps: + - name: Trigger docs tests + run: | + curl -L -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.REPO_DISPATCH_PAT_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/topoteretes/cognee-docs/dispatches \ + -d '{"event_type":"new-main-release","client_payload":{"caller_repo":"'"${GITHUB_REPOSITORY}"'"}}' + + trigger-community-test-suite: + needs: release-pypi-package + if: ${{ github.ref_name == 'main' }} + runs-on: ubuntu-22.04 + steps: + - name: Trigger community tests + run: | + curl -L -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.REPO_DISPATCH_PAT_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/topoteretes/cognee-community/dispatches \ + -d '{"event_type":"new-main-release","client_payload":{"caller_repo":"'"${GITHUB_REPOSITORY}"'"}}' + + notify-discord: + needs: release-github + name: Send Release to Discord + if: ${{ github.ref_name == 'main' }} + runs-on: ubuntu-22.04 + steps: + - name: Send Discord notification + env: + WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }} + TAG: ${{ needs.release-github.outputs.tag }} + VERSION: ${{ needs.release-github.outputs.version }} + run: | + RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/${TAG}" + PAYLOAD=$(jq -n \ + --arg content "||@everyone|| **${TAG}** has been released!" \ + --arg title "Release ${TAG}" \ + --arg url "$RELEASE_URL" \ + --arg description "Version ${VERSION} released from \`${{ github.ref_name }}\`. [View release notes](${RELEASE_URL})" \ + --argjson color 2105893 \ + '{ + content: $content, + embeds: [{ + title: $title, + url: $url, + description: $description, + color: $color, + footer: { text: "Changelog" }, + timestamp: (now | todate) + }] + }') + curl -s -H "Content-Type: application/json" -d "$PAYLOAD" "$WEBHOOK_URL" diff --git a/.github/workflows/release_discord_action.yml b/.github/workflows/release_discord_action.yml new file mode 100644 index 0000000..08d986e --- /dev/null +++ b/.github/workflows/release_discord_action.yml @@ -0,0 +1,23 @@ +name: automation | Send Release to Discord + +on: + release: + types: [published] + +jobs: + github-releases-to-discord: + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Github Releases To Discord + uses: SethCohen/github-releases-to-discord@v1.19.0 + with: + webhook_url: ${{ secrets.WEBHOOK_URL }} + color: "2105893" + username: "Release Changelog" + avatar_url: "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png" + content: "||@everyone||" + footer_title: "Changelog" + footer_icon_url: "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png" + footer_timestamp: true diff --git a/.github/workflows/release_test.yml b/.github/workflows/release_test.yml new file mode 100644 index 0000000..111dfd4 --- /dev/null +++ b/.github/workflows/release_test.yml @@ -0,0 +1,201 @@ +# Long-running, heavy and resource-consuming tests for release validation. +# Runs automatically on PRs targeting main (the dev -> main promotion) and manually +# via workflow_dispatch. Fork PRs are skipped (no access to secrets). +name: Release Test Workflow + +permissions: + contents: read + packages: write + +on: + workflow_dispatch: + inputs: + cognee_version: + required: false + default: local + type: string + description: "Pypi-compatible version of cognee to use. For example, 0.5.2.dev0. `local` (default) - Installing Cognee from local source" + pull_request: + branches: + - main + +concurrency: + group: release-tests-${{ github.head_ref || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + + mcp-test: + name: MCP Tests + if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: ./.github/workflows/test_mcp.yml + secrets: inherit + with: + cognee_version: ${{ inputs.cognee_version || 'local' }} + + cli-test: + name: CLI Tests + if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: ./.github/workflows/cli_tests.yml + secrets: inherit + with: + cognee_version: ${{ inputs.cognee_version || 'local' }} + + cot-retriever-test: + name: COT Retriever Test + if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Install dependencies + run: | + if [ "${{ inputs.cognee_version }}" = "local" ] || [ -z "${{ inputs.cognee_version }}" ]; then + uv sync --locked --all-extras + else + uv sync --locked --all-extras + uv pip install cognee==${{ inputs.cognee_version }} + fi + + - name: Run COT retriever integration test + env: + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + run: | + uv run pytest cognee/tests/integration/retrieval/test_graph_completion_retriever_cot.py -v --timeout=300 + + # 10 parallel users against a live HTTP server with access control enabled: + # per-user dataset isolation, add/cognify/search verification via sentinel chunks + # (LLM-free CHUNKS search), delete + recreate, forget + recreate-same-name. + multi-user-e2e-default: + name: Multi-User Release E2E (default file-based DBs) + if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Check out code + uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Install dependencies + run: | + if [ "${{ inputs.cognee_version }}" = "local" ] || [ -z "${{ inputs.cognee_version }}" ]; then + uv sync --locked --all-extras + else + uv sync --locked --all-extras + uv pip install cognee==${{ inputs.cognee_version }} + fi + + - name: Run multi-user release E2E test + env: + ENV: 'dev' + COGNEE_SKIP_CONNECTION_TEST: 'true' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_release_multi_user_e2e.py + + multi-user-e2e-postgres: + name: Multi-User Release E2E (Postgres graph + PGVector + Postgres) + if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + timeout-minutes: 60 + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out code + uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Install dependencies + run: | + if [ "${{ inputs.cognee_version }}" = "local" ] || [ -z "${{ inputs.cognee_version }}" ]; then + uv sync --locked --all-extras + else + uv sync --locked --all-extras + uv pip install cognee==${{ inputs.cognee_version }} + fi + + - name: Run multi-user release E2E test + env: + ENV: 'dev' + COGNEE_SKIP_CONNECTION_TEST: 'true' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: '127.0.0.1' + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + VECTOR_DB_PROVIDER: 'pgvector' + VECTOR_DB_NAME: 'cognee_db' + VECTOR_DB_HOST: '127.0.0.1' + VECTOR_DB_PORT: 5432 + VECTOR_DB_USERNAME: cognee + VECTOR_DB_PASSWORD: cognee + VECTOR_DATASET_DATABASE_HANDLER: 'pgvector' + GRAPH_DATABASE_PROVIDER: 'postgres' + GRAPH_DATABASE_NAME: 'cognee_db' + GRAPH_DATABASE_HOST: '127.0.0.1' + GRAPH_DATABASE_PORT: 5432 + GRAPH_DATABASE_USERNAME: cognee + GRAPH_DATABASE_PASSWORD: cognee + GRAPH_DATASET_DATABASE_HANDLER: 'postgres_graph' + run: uv run python ./cognee/tests/test_release_multi_user_e2e.py + + load-tests: + if: false + name: Load Tests + uses: ./.github/workflows/load_tests.yml + secrets: inherit + + dev-canary-release: + name: Dev Canary Release + if: ${{ github.event_name == 'workflow_dispatch' }} + needs: load-tests + uses: ./.github/workflows/dev_canary_release.yml + secrets: inherit diff --git a/.github/workflows/require-linear-issue.yml b/.github/workflows/require-linear-issue.yml new file mode 100644 index 0000000..3ddbf8c --- /dev/null +++ b/.github/workflows/require-linear-issue.yml @@ -0,0 +1,40 @@ +name: Require Linear issue + +# Every INTERNAL PR must reference a Linear issue (e.g. COG-123) in its title or +# branch name — that is how Linear links a PR to a ticket. Since Linear does not +# gate merges, enforcement is a required GitHub status check: this job fails when a +# PR has no Linear key, and branch protection blocks the merge until it passes. +# +# Fork / external-contributor PRs are intentionally SKIPPED: they have no Linear +# access, and a skipped required check counts as passing, so this never blocks the +# community. Mark the `linear-issue-check` job as a required status check on +# `main` and `dev` in branch protection. + +on: + pull_request: + # `synchronize` matters: a required check must re-run on the latest commit. + types: [opened, edited, reopened, synchronize] + +permissions: + contents: read + +jobs: + linear-issue-check: # this job name is what you mark "required" in branch protection + # Enforce for internal PRs only. Fork PRs (head repo != base repo) skip this job; + # a skipped required check is treated as passing, so external PRs are not blocked. + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - name: PR title or branch must reference a Linear issue + env: + PR_TITLE: ${{ github.event.pull_request.title }} + HEAD_REF: ${{ github.head_ref }} + run: | + # List ALL your Linear team prefixes — verify against Linear ▸ Settings ▸ Teams. + KEYS='COG|SDK|CLO|COM|ENG' + if echo "$PR_TITLE $HEAD_REF" | grep -Eiq "\b(${KEYS})-[0-9]+\b"; then + echo "Linear issue reference found." + else + echo "::error::PR title or branch must reference a Linear issue, e.g. COG-123." + exit 1 + fi diff --git a/.github/workflows/reusable_notebook.yml b/.github/workflows/reusable_notebook.yml new file mode 100644 index 0000000..8f280a3 --- /dev/null +++ b/.github/workflows/reusable_notebook.yml @@ -0,0 +1,71 @@ +name: test-notebook + +on: + workflow_call: + inputs: + notebook-location: + description: "Location of Jupyter notebook to run" + required: true + type: string + ci-image: + required: false + type: string + default: '' + secrets: + #LLM_MODEL: + # required: true + #LLM_ENDPOINT: + # required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + OPENAI_API_KEY: + required: true + #LLM_API_VERSION: + # required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + +env: + RUNTIME__LOG_LEVEL: ERROR + +jobs: + + run_notebook_test: + name: test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + defaults: + run: + shell: bash + steps: + - name: Check out + uses: actions/checkout@master + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "notebook" + + - name: Execute Jupyter Notebook + env: + ENV: 'dev' + #LLM_MODEL: ${{ secrets.LLM_MODEL }} + #LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} # Use OpenAI Until a multimedia model is deployed and DeepEval support for other models is added + #LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: | + uv run jupyter nbconvert \ + --to notebook \ + --execute ${{ inputs.notebook-location }} \ + --output executed_notebook.ipynb \ + --ExecutePreprocessor.timeout=1200 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000..2067a82 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,78 @@ +# 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. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '35 8 * * 2' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. + if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore + # file_mode: git + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/search_db_tests.yml b/.github/workflows/search_db_tests.yml new file mode 100644 index 0000000..859be8a --- /dev/null +++ b/.github/workflows/search_db_tests.yml @@ -0,0 +1,244 @@ +name: Reusable Search DB Tests + +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + databases: + required: false + type: string + default: "all" + description: "Which vector databases to test (comma-separated list or 'all')" + python-versions: + required: false + type: string + default: '["3.10", "3.11", "3.12", "3.13"]' + description: "Python versions to test (JSON array)" + ci-image: + required: false + type: string + default: '' + description: "Container image for CI (empty string means no container)" + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + run-kuzu-lance-sqlite-search-tests: + name: Search test for Kuzu/LanceDB/Sqlite (Python ${{ matrix.python-version }}) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'kuzu/lance/sqlite') }} + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + fail-fast: false + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Kuzu search Tests + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + VECTOR_DB_PROVIDER: 'lancedb' + DB_PROVIDER: 'sqlite' + run: uv run pytest cognee/tests/test_search_db.py -v --log-level=INFO + + run-neo4j-lance-sqlite-search-tests: + name: Search test for Neo4j/LanceDB/Sqlite (Python ${{ matrix.python-version }}) + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'neo4j/lance/sqlite') }} + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + fail-fast: false + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Neo4j search Tests + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'neo4j' + VECTOR_DB_PROVIDER: 'lancedb' + DB_PROVIDER: 'sqlite' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + run: uv run pytest cognee/tests/test_search_db.py -v --log-level=INFO + + run-kuzu-pgvector-postgres-search-tests: + name: Search test for Kuzu/PGVector/Postgres (Python ${{ matrix.python-version }}) + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'kuzu/pgvector/postgres') }} + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + fail-fast: false + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + extra-dependencies: "postgres" + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Kuzu/PGVector/Postgres Tests + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + VECTOR_DB_PROVIDER: 'pgvector' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run pytest cognee/tests/test_search_db.py -v --log-level=INFO + + run-neo4j-pgvector-postgres-search-tests: + name: Search test for Neo4j/PGVector/Postgres (Python ${{ matrix.python-version }}) + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'neo4j/pgvector/postgres') }} + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + fail-fast: false + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries=5 + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + extra-dependencies: "postgres" + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Neo4j + PGVector + Postgres search Tests + env: + ENV: dev + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'neo4j' + VECTOR_DB_PROVIDER: 'pgvector' + DB_PROVIDER: 'postgres' + GRAPH_DATABASE_URL: ${{ steps.neo4j.outputs.neo4j-url }} + GRAPH_DATABASE_USERNAME: ${{ steps.neo4j.outputs.neo4j-username }} + GRAPH_DATABASE_PASSWORD: ${{ steps.neo4j.outputs.neo4j-password }} + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + DB_NAME: cognee_db + DB_HOST: 127.0.0.1 + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run pytest cognee/tests/test_search_db.py -v --log-level=INFO diff --git a/.github/workflows/slow_e2e_tests.yml b/.github/workflows/slow_e2e_tests.yml new file mode 100644 index 0000000..d89b87c --- /dev/null +++ b/.github/workflows/slow_e2e_tests.yml @@ -0,0 +1,93 @@ +name: Slow End-to-End Tests +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + python-version: + required: false + type: string + default: '3.11.x' + ci-image: + required: false + type: string + default: '' + secrets: + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + test-edge-centered-payload: + name: Test Cognify - Edge Centered Payload + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Edge Centered Payload Test + env: + ENV: 'dev' + TRIPLET_EMBEDDING: True + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_edge_centered_payload.py + + test-deletion-on-default-graph_with_legacy_data_1_default: + name: Delete default graph with legacy data test 1 in Kuzu case + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Run deletion on default graph with legacy data (Kuzu) + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + ENABLE_AUTO_MIGRATIONS: 'false' + run: uv run python ./cognee/tests/test_delete_default_graph_with_legacy_data_1.py diff --git a/.github/workflows/sync_mintlify_docs.yml b/.github/workflows/sync_mintlify_docs.yml new file mode 100644 index 0000000..71c1a78 --- /dev/null +++ b/.github/workflows/sync_mintlify_docs.yml @@ -0,0 +1,169 @@ +name: automation | Sync Mintlify Docs + +on: + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + +jobs: + sync-mintlify-docs: + if: ${{ github.event_name != 'release' || github.event.release.prerelease == false }} + runs-on: ubuntu-22.04 + timeout-minutes: 20 + + steps: + - name: Check out core repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + # Normalize release metadata so the sync script can run unchanged for + # published releases, manual dispatches, and branch-push preview runs. + # This step always produces a body file plus synthetic tag/url/date + # values when the event is not an actual GitHub release. + - name: Prepare release body file + id: release_meta + env: + EVENT_NAME: ${{ github.event_name }} + REF_NAME: ${{ github.ref_name }} + COMMIT_SHA: ${{ github.sha }} + RELEASE_BODY: ${{ github.event.release.body }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + RELEASE_URL: ${{ github.event.release.html_url }} + RELEASE_PUBLISHED_AT: ${{ github.event.release.published_at }} + REPOSITORY: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + BODY_FILE="$(mktemp)" + if [ "${EVENT_NAME}" = "release" ]; then + TAG="${RELEASE_TAG}" + RELEASE_LINK="${RELEASE_URL}" + PUBLISHED_AT="${RELEASE_PUBLISHED_AT}" + DOCS_BRANCH_NAME="automation/sync-mintlify-docs-${RELEASE_TAG}" + printf "%s" "${RELEASE_BODY}" > "${BODY_FILE}" + else + TAG="test-sync-${REF_NAME}-${COMMIT_SHA::7}" + RELEASE_LINK="${SERVER_URL}/${REPOSITORY}/commit/${COMMIT_SHA}" + PUBLISHED_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + DOCS_BRANCH_NAME="${REF_NAME}" + printf "%s\n\n- Source commit: \`%s\`\n- Workflow event: \`%s\`\n- Commit URL: %s\n" \ + "Automated docs sync test run from branch \`${REF_NAME}\`." \ + "${COMMIT_SHA}" \ + "${EVENT_NAME}" \ + "${RELEASE_LINK}" > "${BODY_FILE}" + fi + + SAFE_DOCS_BRANCH_NAME="${DOCS_BRANCH_NAME// /-}" + + echo "body_file=${BODY_FILE}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "release_url=${RELEASE_LINK}" >> "$GITHUB_OUTPUT" + echo "published_at=${PUBLISHED_AT}" >> "$GITHUB_OUTPUT" + echo "docs_branch_name=${SAFE_DOCS_BRANCH_NAME}" >> "$GITHUB_OUTPUT" + + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Install Python + run: uv python install + + - name: Install dependencies + run: uv sync --locked --all-extras + + - name: Check out docs repository + uses: actions/checkout@v6 + with: + repository: topoteretes/cognee-docs + token: ${{ secrets.REPO_DISPATCH_PAT_TOKEN }} + ref: main + path: docs-repo + + - name: Sync OpenAPI and changelog + run: | + uv run python tools/sync_release_docs.py \ + --docs-repo "${GITHUB_WORKSPACE}/docs-repo" \ + --tag "${{ steps.release_meta.outputs.tag }}" \ + --release-url "${{ steps.release_meta.outputs.release_url }}" \ + --published-at "${{ steps.release_meta.outputs.published_at }}" \ + --release-body-file "${{ steps.release_meta.outputs.body_file }}" + + - name: Commit docs changes + id: commit_docs + working-directory: docs-repo + run: | + BRANCH_NAME="${{ steps.release_meta.outputs.docs_branch_name }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -B "${BRANCH_NAME}" + git add cognee_openapi_spec.json changelog.mdx + if git diff --cached --quiet; then + echo "changes_made=false" >> "$GITHUB_OUTPUT" + echo "branch_name=${BRANCH_NAME}" >> "$GITHUB_OUTPUT" + exit 0 + fi + git commit -m "docs: sync ${{ steps.release_meta.outputs.tag }}" + echo "changes_made=true" >> "$GITHUB_OUTPUT" + echo "branch_name=${BRANCH_NAME}" >> "$GITHUB_OUTPUT" + + - name: Push docs branch + if: ${{ steps.commit_docs.outputs.changes_made == 'true' }} + working-directory: docs-repo + env: + GH_TOKEN: ${{ secrets.REPO_DISPATCH_PAT_TOKEN }} + run: | + git push --force-with-lease origin "${{ steps.commit_docs.outputs.branch_name }}" + + - name: Create or update docs pull request + if: ${{ steps.commit_docs.outputs.changes_made == 'true' }} + env: + GH_TOKEN: ${{ secrets.REPO_DISPATCH_PAT_TOKEN }} + EVENT_NAME: ${{ github.event_name }} + RELEASE_TAG: ${{ steps.release_meta.outputs.tag }} + RELEASE_URL: ${{ steps.release_meta.outputs.release_url }} + TARGET_REPO: topoteretes/cognee-docs + HEAD_BRANCH: ${{ steps.commit_docs.outputs.branch_name }} + run: | + if [ "${EVENT_NAME}" = "release" ]; then + PR_TITLE="docs: sync release ${RELEASE_TAG}" + PR_BODY=$(cat <- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Temporal Graph with Kuzu (postgres + pgvector) + env: + ENV: dev + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.OPENAI_MODEL }} + LLM_ENDPOINT: ${{ secrets.OPENAI_ENDPOINT }} + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'kuzu' + VECTOR_DB_PROVIDER: 'pgvector' + DB_PROVIDER: 'postgres' + DB_NAME: 'cognee_db' + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_temporal_graph.py + + run_temporal_graph_neo4j_postgres_pgvector: + name: Temporal Graph test Neo4j (postgres + pgvector) + runs-on: ubuntu-22.04 + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'neo4j/pgvector/postgres') }} + services: + neo4j: + image: neo4j:5.11 + env: + NEO4J_AUTH: neo4j/pleaseletmein + NEO4J_PLUGINS: '["apoc","graph-data-science"]' + ports: + - 7474:7474 + - 7687:7687 + options: >- + --health-cmd="cypher-shell -u neo4j -p pleaseletmein 'RETURN 1'" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: cognee + POSTGRES_DB: cognee_db + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries=5 + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Temporal Graph with Neo4j (postgres + pgvector) + env: + ENV: dev + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.OPENAI_MODEL }} + LLM_ENDPOINT: ${{ secrets.OPENAI_ENDPOINT }} + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + GRAPH_DATABASE_PROVIDER: 'neo4j' + VECTOR_DB_PROVIDER: 'pgvector' + DB_PROVIDER: 'postgres' + GRAPH_DATABASE_URL: bolt://localhost:7687 + GRAPH_DATABASE_USERNAME: neo4j + GRAPH_DATABASE_PASSWORD: pleaseletmein + DB_NAME: cognee_db + DB_HOST: 127.0.0.1 + DB_PORT: 5432 + DB_USERNAME: cognee + DB_PASSWORD: cognee + run: uv run python ./cognee/tests/test_temporal_graph.py diff --git a/.github/workflows/test_different_operating_systems.yml b/.github/workflows/test_different_operating_systems.yml new file mode 100644 index 0000000..895eba5 --- /dev/null +++ b/.github/workflows/test_different_operating_systems.yml @@ -0,0 +1,220 @@ +name: Tests to run on different Operating Systems + +permissions: + contents: read + +on: + workflow_call: + inputs: + python-versions: + required: false + type: string + default: '["3.10.x", "3.12.x", "3.13.x"]' + os: + required: false + type: string + default: '["ubuntu-22.04", "macos-15", "windows-latest"]' + extra-dependencies: + # Space-separated package extras for the unit-test job (e.g. "postgres"). + # Driver-dependent tests importorskip when their extra is absent. + required: false + type: string + default: '' + secrets: + LLM_PROVIDER: + required: true + LLM_MODEL: + required: true + LLM_ENDPOINT: + required: true + LLM_API_KEY: + required: true + LLM_ARGS: + required: false + LLM_API_VERSION: + required: true + EMBEDDING_PROVIDER: + required: true + EMBEDDING_MODEL: + required: true + EMBEDDING_API_KEY: + required: true + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + +jobs: + run-unit-tests: + name: Unit tests ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + os: ${{ fromJSON(inputs.os) }} + fail-fast: false + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + extra-dependencies: ${{ inputs.extra-dependencies }} + + - name: Run unit tests + shell: bash + run: uv run pytest cognee/tests/unit/ --timeout=300 --timeout-method=thread + env: + PYTHONUTF8: 1 + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + + run-library-test: + name: Library test ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + os: ${{ fromJSON(inputs.os) }} + fail-fast: false + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + + - name: Run default basic pipeline + shell: bash + env: + PYTHONUTF8: 1 + LLM_PROVIDER: openai + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + + EMBEDDING_PROVIDER: openai + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_library.py + + run-build-test: + name: Build test ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + os: ${{ fromJSON(inputs.os) }} + fail-fast: false + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + + - name: Build with uv + shell: bash + run: uv build + + - name: Install Package + if: ${{ !contains(matrix.os, 'windows-latest') }} + run: | + cd dist + pip install *.whl + + run-soft-deletion-test: + name: Soft Delete test ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + os: ${{ fromJSON(inputs.os) }} + fail-fast: false + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + + - name: Path setup + if: ${{ matrix.os == 'windows-latest' }} + shell: bash + run: | + PATH=$(printf '%s' "$PATH" | tr ':' $'\n' | grep -vi '/git/usr/bin' | paste -sd: -) + export PATH + + - name: Run Soft Deletion Tests + env: + ENV: 'dev' + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} # Test needs OpenAI endpoint to handle multimedia + LLM_ARGS: ${{ secrets.LLM_ARGS }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_default_graph.py + + run-hard-deletion-test: + name: Custom Graph Delete test ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + python-version: ${{ fromJSON(inputs.python-versions) }} + os: ${{ fromJSON(inputs.os) }} + fail-fast: false + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + + - name: Run Custom Graph Deletion Test + env: + ENV: 'dev' + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} # Test needs OpenAI endpoint to handle multimedia + LLM_ARGS: ${{ secrets.LLM_ARGS }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_delete_custom_graph.py diff --git a/.github/workflows/test_llamacpp.yml b/.github/workflows/test_llamacpp.yml new file mode 100644 index 0000000..2c57675 --- /dev/null +++ b/.github/workflows/test_llamacpp.yml @@ -0,0 +1,61 @@ +name: test | ollama + +on: + workflow_call: + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + + run_llama-cpp_test: + + # needs ~4 Gb RAM for the GGUF model in a container which the smallest runner has + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.13.x' + extra-dependencies: postgres llama-cpp + + - name: Install torch dependency + run: | + uv add torch + + - name: Download Phi-3.5 GGUF model from S3 + # Mirrored from huggingface.co/bartowski/Phi-3.5-mini-instruct-GGUF (MIT) + # into our bucket to avoid HuggingFace 429 rate limits in CI. + # Phi-3.5-mini reliably emits the required per-node `description`; + # the previous Phi-3-mini-q4 dropped it, failing extraction. + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + AWS_DEFAULT_REGION: eu-west-1 + BUCKET: github-runner-cognee-tests + MODEL_KEY: nightly_ci_artifacts/huggingface_models/Phi-3.5-mini-instruct-Q4_K_M.gguf + MODEL_SHA256: e4165e3a71af97f1b4820da61079826d8752a2088e313af0c7d346796c38eff5 + run: | + set -euo pipefail + aws s3 cp "s3://$BUCKET/$MODEL_KEY" ./Phi-3.5-mini-instruct-Q4_K_M.gguf + echo "$MODEL_SHA256 ./Phi-3.5-mini-instruct-Q4_K_M.gguf" | sha256sum -c - + + - name: Run example test + env: + PYTHONFAULTHANDLER: 1 + LLM_PROVIDER: "llama_cpp" + LLAMA_CPP_MODEL_PATH: "./Phi-3.5-mini-instruct-Q4_K_M.gguf" + LLM_ENDPOINT: "" + LLAMA_CPP_N_CTX: 4096 + EMBEDDING_PROVIDER: "openai" + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + EMBEDDING_MODEL: "openai/text-embedding-3-large" + EMBEDDING_DIMENSIONS: "3072" + EMBEDDING_MAX_TOKENS: "8191" + STRUCTURED_OUTPUT_FRAMEWORK: "instructor" + LLM_INSTRUCTOR_MODE: "" + run: uv run python ./examples/demos/simple_cognee_example.py diff --git a/.github/workflows/test_llms.yml b/.github/workflows/test_llms.yml new file mode 100644 index 0000000..bcecf93 --- /dev/null +++ b/.github/workflows/test_llms.yml @@ -0,0 +1,220 @@ +name: LLM Test Suites + +permissions: + contents: read + packages: read + +on: + workflow_call: + inputs: + ci-image: + required: false + type: string + default: '' + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + test-gemini: + name: Run Gemini Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Gemini Simple Example + env: + LLM_PROVIDER: "gemini" + LLM_API_KEY: ${{ secrets.GEMINI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MODEL: "gemini/gemini-3-flash-preview" + EMBEDDING_PROVIDER: "gemini" + EMBEDDING_API_KEY: ${{ secrets.GEMINI_API_KEY }} + EMBEDDING_MODEL: "gemini/gemini-embedding-001" + EMBEDDING_DIMENSIONS: "768" + EMBEDDING_MAX_TOKENS: "8076" + run: uv run python ./examples/demos/simple_cognee_example.py + + test-fastembed: + name: Run Fastembed Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Fastembed Simple Example + env: + LLM_PROVIDER: "openai" + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_PROVIDER: "fastembed" + EMBEDDING_MODEL: "sentence-transformers/all-MiniLM-L6-v2" + EMBEDDING_DIMENSIONS: "384" + EMBEDDING_MAX_TOKENS: "256" + run: uv run python ./examples/demos/simple_cognee_example.py + + test-openrouter: + name: Run OpenRouter Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run OpenRouter Simple Example + env: + LLM_PROVIDER: "custom" + LLM_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MODEL: "openrouter/x-ai/grok-4.3" + LLM_ENDPOINT: "https://openrouter.ai/api/v1" + EMBEDDING_PROVIDER: "openai" + EMBEDDING_API_KEY: ${{ secrets.OPENAI_API_KEY }} + EMBEDDING_MODEL: "openai/text-embedding-3-large" + EMBEDDING_DIMENSIONS: "3072" + EMBEDDING_MAX_TOKENS: "8191" + run: uv run python ./examples/demos/simple_cognee_example.py + + test-bedrock-api-key: + name: Run Bedrock API Key Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "aws" + + - name: Run Bedrock API Key Simple Example + env: + LLM_PROVIDER: "bedrock" + LLM_API_KEY: ${{ secrets.BEDROCK_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MODEL: "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" + LLM_MAX_TOKENS: "16384" + AWS_REGION_NAME: "eu-west-1" + EMBEDDING_PROVIDER: "bedrock" + EMBEDDING_API_KEY: ${{ secrets.BEDROCK_API_KEY }} + EMBEDDING_MODEL: "amazon.titan-embed-text-v2:0" + EMBEDDING_DIMENSIONS: "1024" + EMBEDDING_MAX_TOKENS: "8191" + run: uv run python ./examples/demos/simple_cognee_example.py + + test-bedrock-aws-credentials: + name: Run Bedrock AWS Credentials Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "aws" + + - name: Run Bedrock AWS Credentials Simple Example + env: + LLM_PROVIDER: "bedrock" + LLM_MODEL: "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" + LLM_MAX_TOKENS: "16384" + AWS_REGION_NAME: "eu-west-1" + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + EMBEDDING_PROVIDER: "bedrock" + EMBEDDING_API_KEY: ${{ secrets.BEDROCK_API_KEY }} + EMBEDDING_MODEL: "amazon.titan-embed-text-v2:0" + EMBEDDING_DIMENSIONS: "1024" + EMBEDDING_MAX_TOKENS: "8191" + run: uv run python ./examples/demos/simple_cognee_example.py + + test-bedrock-aws-profile: + name: Run Bedrock AWS Profile Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "aws" + + - name: Configure AWS Profile + run: | + mkdir -p ~/.aws + cat > ~/.aws/credentials << EOF + [bedrock-test] + aws_access_key_id = ${{ secrets.AWS_ACCESS_KEY_ID }} + aws_secret_access_key = ${{ secrets.AWS_SECRET_ACCESS_KEY }} + EOF + + - name: Run Bedrock AWS Profile Simple Example + env: + LLM_PROVIDER: "bedrock" + LLM_MODEL: "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" + LLM_MAX_TOKENS: "16384" + AWS_PROFILE_NAME: "bedrock-test" + AWS_REGION_NAME: "eu-west-1" + EMBEDDING_PROVIDER: "bedrock" + EMBEDDING_MODEL: "amazon.titan-embed-text-v2:0" + EMBEDDING_DIMENSIONS: "1024" + EMBEDDING_MAX_TOKENS: "8191" + run: uv run python ./examples/demos/simple_cognee_example.py + + test-claude-anthropic: + name: Run Anthropic Claude LLM Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Run Claude Anthropic LLM Simple Example + env: + LLM_PROVIDER: "anthropic" + # Note: If model becomes outdated update docs with new model as well + LLM_MODEL: "anthropic/claude-sonnet-4-5-20250929" + LLM_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_MAX_TOKENS: "16384" + EMBEDDING_API_KEY: ${{ secrets.OPENAI_API_KEY }} + EMBEDDING_MODEL: "openai/text-embedding-3-large" + EMBEDDING_DIMENSIONS: "3072" + EMBEDDING_MAX_TOKENS: "8191" + run: uv run python ./examples/demos/simple_cognee_example.py diff --git a/.github/workflows/test_mcp.yml b/.github/workflows/test_mcp.yml new file mode 100644 index 0000000..8fce87b --- /dev/null +++ b/.github/workflows/test_mcp.yml @@ -0,0 +1,64 @@ +name: test | mcp + +on: + workflow_call: + inputs: + cognee_version: + required: false + type: string + default: "local" # local - Installing Cognee from local source + description: "Pypi-compatible version of cognee to use. For example, 0.5.2.dev0. `local` (default) - Installing Cognee from local source" + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + test-mcp: + name: Run MCP Test + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python-version }} + + - name: Install UV + shell: bash + run: | + python -m pip install --upgrade pip + pip install uv + + - name: Install dependencies + shell: bash + working-directory: cognee-mcp + run: uv sync + + - name: Install Cognee + uses: ./.github/actions/install_cognee + with: + python-version: ${{ inputs.python-version }} + cognee_version: ${{ inputs.cognee_version }} + + - name: Run MCP unit tests + working-directory: cognee-mcp + run: uv run --no-sync pytest tests/ + + - name: Run MCP test + env: + ENV: 'dev' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + working-directory: cognee-mcp + run: | + echo "Cognee Version:" + uv pip show cognee | grep Version + uv run --no-sync python ./src/test_client.py diff --git a/.github/workflows/test_ollama.yml b/.github/workflows/test_ollama.yml new file mode 100644 index 0000000..138ca4e --- /dev/null +++ b/.github/workflows/test_ollama.yml @@ -0,0 +1,111 @@ +name: test | ollama + +on: + workflow_call: + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + + run_ollama_test: + + # TODO: needs 32 Gb RAM for phi4 in a container — GitHub-hosted larger runner + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + + - name: Install torch dependency + run: | + uv add torch + + - name: Start Ollama container + run: | + docker run -d --name ollama -p 11434:11434 ollama/ollama + sleep 5 + docker exec -d ollama bash -c "ollama serve --openai" + + - name: Check Ollama logs + run: docker logs ollama + + - name: Wait for Ollama to be ready + run: | + for i in {1..30}; do + if curl -s http://localhost:11434/v1/models > /dev/null; then + echo "Ollama is ready" + exit 0 + fi + echo "Waiting for Ollama... attempt $i" + sleep 2 + done + echo "Ollama failed to start" + exit 1 + + - name: Pull required Ollama models + run: | + curl -X POST http://localhost:11434/api/pull -d '{"name": "phi4"}' + curl -X POST http://localhost:11434/api/pull -d '{"name": "qwen3-embedding:latest"}' + + - name: Call ollama API + run: | + curl -X POST http://localhost:11434/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "phi4", + "stream": false, + "messages": [ + { "role": "system", "content": "You are a helpful assistant." }, + { "role": "user", "content": "Whatever I say, answer with Yes." } + ] + }' + curl -X POST http://127.0.0.1:11434/api/embed \ + -H "Content-Type: application/json" \ + -d '{ + "model": "qwen3-embedding:latest", + "input": "This is a test sentence to generate an embedding." + }' + + - name: Dump Docker logs + run: | + docker ps + docker logs $(docker ps --filter "ancestor=ollama/ollama" --format "{{.ID}}") + + - name: Download embedding tokenizer from S3 + # Mirrored from huggingface.co/Qwen/Qwen3-Embedding-8B (Apache-2.0) so the + # embedding tokenizer loads offline and never hits HuggingFace 429s in CI. + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + AWS_DEFAULT_REGION: eu-west-1 + BUCKET: github-runner-cognee-tests + TOKENIZER_KEY: nightly_ci_artifacts/huggingface_models/qwen3-embedding-tokenizer.tar.gz + TOKENIZER_SHA256: 8f5834d8791c8da03220feaccc2fe9c443e23b1092dcd99c576ef613990bbd00 + run: | + set -euo pipefail + aws s3 cp "s3://$BUCKET/$TOKENIZER_KEY" tokenizer.tar.gz + echo "$TOKENIZER_SHA256 tokenizer.tar.gz" | sha256sum -c - + tar -xzf tokenizer.tar.gz -C "$GITHUB_WORKSPACE" + + - name: Run example test + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + PYTHONFAULTHANDLER: 1 + LLM_PROVIDER: "ollama" + LLM_API_KEY: "ollama" + LLM_ENDPOINT: "http://localhost:11434/v1/" + LLM_MODEL: "phi4" + EMBEDDING_PROVIDER: "ollama" + EMBEDDING_MODEL: "qwen3-embedding:latest" + EMBEDDING_ENDPOINT: "http://localhost:11434/api/embed" + EMBEDDING_DIMENSIONS: "4096" + # Load the tokenizer from the S3-mirrored local dir, fully offline. + HUGGINGFACE_TOKENIZER: "${{ github.workspace }}/qwen3-embedding-tokenizer" + HF_HUB_OFFLINE: "1" + TRANSFORMERS_OFFLINE: "1" + run: uv run python ./examples/demos/simple_cognee_example.py diff --git a/.github/workflows/test_s3_file_storage.yml b/.github/workflows/test_s3_file_storage.yml new file mode 100644 index 0000000..79e2d83 --- /dev/null +++ b/.github/workflows/test_s3_file_storage.yml @@ -0,0 +1,52 @@ +name: test | s3 file storage + +on: + workflow_call: + inputs: + ci-image: + required: false + type: string + default: '' + +permissions: + contents: read + packages: read + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + test-s3-storage: + name: Run S3 File Storage Test + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11.x' + extra-dependencies: "aws" + + - name: Run S3 File Storage Test + env: + STORAGE_BACKEND: s3 + ENABLE_BACKEND_ACCESS_CONTROL: True + AWS_REGION: eu-west-1 + AWS_ENDPOINT_URL: https://s3-eu-west-1.amazonaws.com + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_DEV_USER_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_DEV_USER_SECRET_KEY }} + STORAGE_BUCKET_NAME: github-runner-cognee-tests + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_s3_file_storage.py diff --git a/.github/workflows/test_suites.yml b/.github/workflows/test_suites.yml new file mode 100644 index 0000000..2ba1415 --- /dev/null +++ b/.github/workflows/test_suites.yml @@ -0,0 +1,413 @@ +name: Test Suites +permissions: + contents: read + packages: write + +on: + push: + branches: [ main, dev ] + pull_request: + branches: [ main, dev ] + types: [opened, synchronize, reopened, labeled] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + +jobs: + # ── Build pre-baked CI container ────────────────────────────────────── + build-ci-env: + name: Build CI Environment + runs-on: ubuntu-latest + if: >- + github.event_name == 'push' || github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository) + outputs: + ci-image: ${{ steps.set-image.outputs.image }} + steps: + - name: Sparse checkout (deps only) + uses: actions/checkout@v6 + with: + sparse-checkout: | + pyproject.toml + uv.lock + README.md + Dockerfile.ci + .dockerignore.ci + sparse-checkout-cone-mode: false + + - name: Compute dep hash and image tag + id: set-image + run: | + DEP_HASH=$(sha256sum pyproject.toml uv.lock | sha256sum | head -c 16) + IMAGE="ghcr.io/${{ github.repository }}/ci-env:deps-${DEP_HASH}" + echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" + echo "dep_hash=${DEP_HASH}" >> "$GITHUB_OUTPUT" + echo "Image tag: ${IMAGE}" + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if image already exists + id: check + run: | + if docker manifest inspect "${{ steps.set-image.outputs.image }}" > /dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + echo "Image already exists, skipping build." + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Image not found, will build." + fi + + - name: Set up Docker Buildx + if: steps.check.outputs.exists != 'true' + uses: docker/setup-buildx-action@v3 + + - name: Build and push CI image + if: steps.check.outputs.exists != 'true' + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.ci + push: true + tags: ${{ steps.set-image.outputs.image }} + cache-from: type=registry,ref=ghcr.io/${{ github.repository }}/ci-env:buildcache + cache-to: type=registry,ref=ghcr.io/${{ github.repository }}/ci-env:buildcache,mode=max + + pre-test: + name: basic checks + uses: ./.github/workflows/pre_test.yml + + basic-tests: + name: Basic Tests + uses: ./.github/workflows/basic_tests.yml + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + e2e-tests: + name: End-to-End Tests + uses: ./.github/workflows/e2e_tests.yml + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + cli-tests: + name: CLI Tests + uses: ./.github/workflows/cli_tests.yml + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + slow-e2e-tests: + name: Slow End-to-End Tests + uses: ./.github/workflows/slow_e2e_tests.yml + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + graph-db-tests: + name: Graph Database Tests + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/graph_db_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + vector-db-tests: + name: Vector DB Tests + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/vector_db_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + example-tests: + name: Example Tests + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/examples_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + notebook-tests: + name: Notebook Tests + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/notebooks_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + different-os-tests-basic: + name: OS and Python Tests Ubuntu + uses: ./.github/workflows/test_different_operating_systems.yml + with: + python-versions: '["3.10.x", "3.11.x", "3.12.x", "3.13.x", "3.14.x"]' + os: '["ubuntu-22.04"]' + # postgres so the SQL cache adapter tests run instead of skipping; omitted + # on macos/windows (extended) where psycopg2 has no prebuilt wheels. + extra-dependencies: 'postgres' + secrets: inherit + + different-os-tests-extended: + name: OS and Python Tests Extended + uses: ./.github/workflows/test_different_operating_systems.yml + with: + python-versions: '["3.13.x"]' + os: '["macos-15", "windows-latest"]' + secrets: inherit + + llm-tests: + name: LLM Test Suite + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/test_llms.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + s3-file-storage-test: + name: S3 File Storage Test + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/test_s3_file_storage.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + integration-tests: + name: Run Integration Tests + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/integration_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + mcp-test: + name: MCP Tests + uses: ./.github/workflows/test_mcp.yml + secrets: inherit + + docker-compose-test: + name: Docker Compose Test + uses: ./.github/workflows/docker_compose.yml + secrets: inherit + + docker-ci-test: + name: Docker CI test + uses: ./.github/workflows/backend_docker_build_test.yml + secrets: inherit + + temporal-graph-tests: + name: Temporal Graph Test + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/temporal_graph_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + search-db-tests: + name: Search Test on Different DBs + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/search_db_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + relational-db-migration-tests: + name: Relational DB Migration Tests + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/relational_db_migration_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + # NOTE: intentionally excluded from the `notify` aggregator below — this is + # external infra (Modal + Azure secrets), informational only; it must not gate + # merges. (`continue-on-error` is not allowed on a reusable-workflow job.) + distributed-tests: + name: Distributed Cognee Test + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/distributed_test.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + db-examples-tests: + name: DB Examples Tests + needs: [ build-ci-env ] + if: ${{ !cancelled() }} + uses: ./.github/workflows/db_examples_tests.yml + with: + ci-image: ${{ needs.build-ci-env.outputs.ci-image || '' }} + secrets: inherit + + notify: + name: Test Completion Status + needs: [ + pre-test, + basic-tests, + e2e-tests, + cli-tests, + slow-e2e-tests, + graph-db-tests, + vector-db-tests, + example-tests, + notebook-tests, + different-os-tests-basic, + different-os-tests-extended, + llm-tests, + s3-file-storage-test, + integration-tests, + mcp-test, + docker-compose-test, + docker-ci-test, + temporal-graph-tests, + search-db-tests, + relational-db-migration-tests, + db-examples-tests, + ] + runs-on: ubuntu-latest + if: ${{ !cancelled() }} + steps: + - name: Check Status + run: | + if [[ "${{ needs.pre-test.result }}" == "success" && + "${{ needs.basic-tests.result }}" == "success" && + "${{ needs.e2e-tests.result }}" == "success" && + "${{ needs.cli-tests.result }}" == "success" && + "${{ needs.slow-e2e-tests.result }}" == "success" && + "${{ needs.graph-db-tests.result }}" == "success" && + "${{ needs.vector-db-tests.result }}" == "success" && + "${{ needs.example-tests.result }}" == "success" && + "${{ needs.notebook-tests.result }}" == "success" && + "${{ needs.different-os-tests-basic.result }}" == "success" && + "${{ needs.different-os-tests-extended.result }}" == "success" && + "${{ needs.llm-tests.result }}" == "success" && + "${{ needs.s3-file-storage-test.result }}" == "success" && + "${{ needs.integration-tests.result }}" == "success" && + "${{ needs.mcp-test.result }}" == "success" && + "${{ needs.docker-compose-test.result }}" == "success" && + "${{ needs.docker-ci-test.result }}" == "success" && + "${{ needs.temporal-graph-tests.result }}" == "success" && + "${{ needs.search-db-tests.result }}" == "success" && + "${{ needs.relational-db-migration-tests.result }}" == "success" && + "${{ needs.db-examples-tests.result }}" == "success" ]]; then + echo "All test suites completed successfully!" + else + echo "One or more test suites failed." + exit 1 + fi + + # ── Advisory Claude Code review — pulls the private skill from cognee-ci at runtime ── + # The rubric + Slack map live in the private cognee-ci repo; this job carries no review + # logic. Runs only after the suite is green (needs: notify), core-team PRs only, advisory. + claude-review: + name: Claude Code Review + needs: [ notify ] + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + continue-on-error: true + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout PR + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Core-team gate + id: gate + env: + AUTHOR: ${{ github.event.pull_request.user.login }} + run: | + author_lc="$(printf '%s' "$AUTHOR" | tr '[:upper:]' '[:lower:]')" + if grep -vE '^[[:space:]]*#' .github/core-team.txt \ + | sed 's/^@//' | tr '[:upper:]' '[:lower:]' | grep -qxF "$author_lc"; then + echo "run=true" >> "$GITHUB_OUTPUT" + else + echo "run=false" >> "$GITHUB_OUTPUT" + echo "PR author '$AUTHOR' not in core-team.txt — skipping." + fi + + - name: Mint private-repo token + if: ${{ steps.gate.outputs.run == 'true' }} + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.CI_APP_ID }} + private-key: ${{ secrets.CI_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: cognee-ci + + - name: Fetch private review skill + slack map + if: ${{ steps.gate.outputs.run == 'true' }} + uses: actions/checkout@v6 + with: + repository: ${{ github.repository_owner }}/cognee-ci + token: ${{ steps.app-token.outputs.token }} + path: .ci-private + + - name: Stage skill into the user skills dir + if: ${{ steps.gate.outputs.run == 'true' }} + run: | + # Stage into the USER skills dir ($HOME/.claude), NOT the repo's .claude/. + # claude-code-action sanitizes the repo .claude for untrusted PR heads + # (it moves the working-tree .claude to .claude-pr/ and restores a clean + # .claude from the base branch), which wipes the staged private skill and + # leaves /pr-review unresolved -> 0 turns, nothing posted. $HOME/.claude is + # untouched by that sanitization and is loaded via settingSources: [user,...]. + mkdir -p "$HOME/.claude/skills" + cp -r .ci-private/.claude/skills/pr-review "$HOME/.claude/skills/" + + - name: Claude Code review + if: ${{ steps.gate.outputs.run == 'true' }} + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + prompt: "/pr-review" + claude_args: >- + --allowed-tools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Read,Glob,Grep" + --max-turns 20 + --model claude-sonnet-4-5-20250929 + + - name: DM review summary to PR author on Slack + if: ${{ steps.gate.outputs.run == 'true' }} + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + AUTHOR: ${{ github.event.pull_request.user.login }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_URL: ${{ github.event.pull_request.html_url }} + PR_TITLE: ${{ github.event.pull_request.title }} + REPO: ${{ github.repository }} + run: python3 .ci-private/scripts/pr_review_slack_notify.py diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml new file mode 100644 index 0000000..1881201 --- /dev/null +++ b/.github/workflows/update-contributors.yml @@ -0,0 +1,37 @@ +name: community | contributors leaderboard + +on: + schedule: + - cron: "0 0 * * 1" # Runs every Monday + workflow_dispatch: # Allows manual trigger + +jobs: + update-contributors: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v6 + + - name: Generate Contributor List + run: | + echo "## 💫 Contributors" > CONTRIBUTORS.md + echo "" >> CONTRIBUTORS.md + echo "Thanks to our amazing contributors! 💖" >> CONTRIBUTORS.md + echo "" >> CONTRIBUTORS.md + echo '' >> CONTRIBUTORS.md + echo ' ' >> CONTRIBUTORS.md + echo '' >> CONTRIBUTORS.md + echo "" >> CONTRIBUTORS.md + echo "## 🏆 Top Contributors" >> CONTRIBUTORS.md + echo "" >> CONTRIBUTORS.md + echo "| Rank | Contributor | Contributions |" >> CONTRIBUTORS.md + echo "|------|------------|---------------|" >> CONTRIBUTORS.md + git shortlog -sne | sort -rn | head -10 | awk '{print "| "NR" | ["$2"](https://github.com/"$2") | "$1" Commits |"}' >> CONTRIBUTORS.md + + - name: Commit and Push Changes + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions@users.noreply.github.com" + git add CONTRIBUTORS.md + git commit -m "Update contributors list" + git push diff --git a/.github/workflows/vector_db_tests.yml b/.github/workflows/vector_db_tests.yml new file mode 100644 index 0000000..7c1bb07 --- /dev/null +++ b/.github/workflows/vector_db_tests.yml @@ -0,0 +1,153 @@ +name: Reusable Vector DB Tests + +on: + workflow_call: + inputs: + databases: + required: false + type: string + default: "all" + description: "Which vector databases to test (comma-separated list or 'all')" + ci-image: + required: false + type: string + default: '' + secrets: + + POSTGRES_PASSWORD: + required: false + +env: + COGNEE_SKIP_CONNECTION_TEST: 'true' + +jobs: + run-postgres-tests: + name: PostgreSQL Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'postgres') }} + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Run PGVector Tests + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + run: uv run python ./cognee/tests/test_pgvector.py + + run-pgvector-multi-user-tests: + name: PGVector Multi-User Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + if: ${{ inputs.databases == 'all' || contains(inputs.databases, 'postgres') }} + services: + postgres: + image: ghcr.io/topoteretes/pgvector:pg17 + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + POSTGRES_USER: cognee + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: cognee_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + extra-dependencies: "postgres" + + - name: Run PGVector Permissions Tests + env: + ENV: 'dev' + ENABLE_BACKEND_ACCESS_CONTROL: 'true' + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + VECTOR_DB_HOST: ${{ inputs.ci-image != '' && 'postgres' || '127.0.0.1' }} + VECTOR_DB_PORT: 5432 + VECTOR_DB_USERNAME: cognee + VECTOR_DB_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + VECTOR_DATASET_DATABASE_HANDLER: pgvector + run: uv run python ./cognee/tests/test_permissions.py + + run-lancedb-tests: + name: LanceDB Tests + runs-on: ubuntu-22.04 + container: ${{ inputs.ci-image != '' && fromJSON(format('{{"image":"{0}","credentials":{{"username":"{1}","password":"{2}"}}}}', inputs.ci-image, github.actor, github.token)) || null }} + steps: + - name: Check out + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ inputs.python-version }} + + - name: Run LanceDB Tests + env: + ENV: 'dev' + # Cap cached DB adapters + DATABASE_MAX_LRU_CACHE_SIZE: 1 + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_ENDPOINT: ${{ secrets.LLM_ENDPOINT }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: ${{ secrets.LLM_API_VERSION }} + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + run: uv run python ./cognee/tests/test_lancedb.py diff --git a/.github/workflows/weighted_edges_tests.yml b/.github/workflows/weighted_edges_tests.yml new file mode 100644 index 0000000..ab3e7d5 --- /dev/null +++ b/.github/workflows/weighted_edges_tests.yml @@ -0,0 +1,169 @@ +name: Weighted Edges Tests + +on: + push: + branches: [ main, dev, weighted_edges] + paths: + - 'cognee/modules/graph/utils/get_graph_from_model.py' + - 'cognee/infrastructure/engine/models/Edge.py' + - 'cognee/tests/unit/interfaces/graph/test_weighted_edges.py' + - 'examples/demos/dynamic_multiple_weighted_edges_example.py' + - '.github/workflows/weighted_edges_tests.yml' + pull_request: + branches: [ main, dev ] + paths: + - 'cognee/modules/graph/utils/get_graph_from_model.py' + - 'cognee/infrastructure/engine/models/Edge.py' + - 'cognee/tests/unit/interfaces/graph/test_weighted_edges.py' + - 'examples/demos/dynamic_multiple_weighted_edges_example.py' + - '.github/workflows/weighted_edges_tests.yml' + +env: + RUNTIME__LOG_LEVEL: ERROR + ENV: 'dev' + +jobs: + test-weighted-edges-functionality: + name: Test Weighted Edges Core Functionality + runs-on: ubuntu-22.04 + strategy: + matrix: + python-version: ['3.11', '3.12'] + env: + LLM_PROVIDER: openai + LLM_MODEL: gpt-5-mini + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: ${{ matrix.python-version }} + + - name: Run Weighted Edges Unit Tests + run: | + uv run pytest cognee/tests/unit/interfaces/graph/test_weighted_edges.py -v --tb=short + + - name: Run Standard Graph Tests (Regression) + run: | + uv run pytest cognee/tests/unit/interfaces/graph/get_graph_from_model_unit_test.py -v --tb=short + + test-with-different-databases: + name: Test Weighted Edges with Different Graph Databases + runs-on: ubuntu-22.04 + strategy: + matrix: + database: ['kuzu', 'neo4j'] + include: + - database: kuzu + graph_db_provider: "kuzu" + - database: neo4j + graph_db_provider: "neo4j" + env: + LLM_PROVIDER: openai + LLM_MODEL: gpt-5-mini + LLM_ENDPOINT: https://api.openai.com/v1 + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: "2024-02-01" + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11' + + - name: Setup Neo4j with GDS + uses: ./.github/actions/setup_neo4j + id: neo4j + + - name: Dependencies already installed + run: echo "Dependencies already installed in setup" + + - name: Run Weighted Edges Tests + env: + ENABLE_BACKEND_ACCESS_CONTROL: 'false' + GRAPH_DATABASE_PROVIDER: ${{ matrix.graph_db_provider }} + GRAPH_DATABASE_URL: ${{ matrix.graph_db_provider == 'neo4j' && steps.neo4j.outputs.neo4j-url || '' }} + GRAPH_DATABASE_USERNAME: ${{ matrix.graph_db_provider == 'neo4j' && steps.neo4j.outputs.neo4j-username || '' }} + GRAPH_DATABASE_PASSWORD: ${{ matrix.graph_db_provider == 'neo4j' && steps.neo4j.outputs.neo4j-password || '' }} + run: | + uv run pytest cognee/tests/unit/interfaces/graph/test_weighted_edges.py -v --tb=short + + test-examples: + name: Test Weighted Edges Examples + runs-on: ubuntu-22.04 + env: + LLM_PROVIDER: openai + LLM_MODEL: gpt-5-mini + LLM_ENDPOINT: https://api.openai.com/v1 + LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_ARGS: ${{ secrets.LLM_ARGS }} + LLM_API_VERSION: "2024-02-01" + EMBEDDING_DIMENSIONS: 300 + EMBEDDING_MODEL: ${{ secrets.EMBEDDING_MODEL }} + EMBEDDING_API_KEY: ${{ secrets.EMBEDDING_API_KEY }} + + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11' + + - name: Test Weighted Edges Example + run: | + uv run python examples/demos/dynamic_multiple_weighted_edges_example.py + + - name: Verify Visualization File Created + run: | + if [ -f "examples/demos/.artifacts/dunder_mifflin_company_graph.html" ]; then + echo "✅ Visualization file created successfully" + ls -la examples/demos/.artifacts/dunder_mifflin_company_graph.html + else + echo "❌ Visualization file not found" + exit 1 + fi + + + + code-quality: + name: Code Quality for Weighted Edges + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Cognee Setup + uses: ./.github/actions/cognee_setup + with: + python-version: '3.11' + + - name: Run Linting on Weighted Edges Files + uses: astral-sh/ruff-action@v3 + with: + args: "check cognee/modules/graph/utils/get_graph_from_model.py cognee/tests/unit/interfaces/graph/test_weighted_edges.py examples/demos/dynamic_multiple_weighted_edges_example.py" + + - name: Run Formatting Check on Weighted Edges Files + uses: astral-sh/ruff-action@v3 + with: + args: "format --check cognee/modules/graph/utils/get_graph_from_model.py cognee/tests/unit/interfaces/graph/test_weighted_edges.py examples/demos/dynamic_multiple_weighted_edges_example.py" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71a1ac0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,213 @@ +.data +.env +.local.env +.prod.env +cognee/.data/ + +code_pipeline_output*/ + +# Test output files +test_outputs/ + +*.lance/ +.DS_Store +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +full_run.ipynb + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +.python-version +cognee-mcp/.python-version +MANIFEST + +# cognee-mcp app bundle (built by `npm run build` under cognee-mcp/apps-src/). +# Generated artifact — see cognee-mcp/README.md for the build step. +cognee-mcp/src/app_bundles/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Cognee logs directory - keep directory, ignore contents +logs/* +!logs/.gitkeep +!logs/README.md + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.env.local +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +mise.toml +deployment/helm/values-local.yml + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +.vscode/ +cognee/data/ +cognee/cache/ + +# Default cognee system directory, used in development +.cognee_system/ +.data_storage/ +.cognee_cache/ +.artifacts/ +.anon_id + +node_modules/ + +# Evals +SWE-bench_testsample/ + +# ChromaDB Data +.chromadb_data/ + +deployment/helm/values-local.yml + +# Performance test results +cognee/tests/performance/results diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..c8aef74 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,9 @@ +pull_request_rules: + - name: Backport to main when backport_main label is set + conditions: + - label=backport_main + - base=dev + actions: + backport: + branches: + - main diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..426b8f2 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + exclude: ^deployment/helm/templates/ + - id: check-added-large-files + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.15.11 + hooks: + # Run the linter. + - id: ruff + types_or: [python, pyi] + # Run the formatter. + - id: ruff-format + types_or: [python, pyi] diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..376bbab --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,130 @@ +## Repository Guidelines + +This document summarizes how to work with the cognee repository: how it’s organized, how to build, test, lint, and contribute. It mirrors our actual tooling and CI while providing quick commands for local development. + +## Project Structure & Module Organization + +- `cognee/`: Core Python library and API. + - `api/`: FastAPI application and versioned routers (add, cognify, memify, search, delete, users, datasets, responses, visualize, settings, sync, update, checks). + - `cli/`: CLI entry points and subcommands invoked via `cognee` / `cognee-cli`. + - `infrastructure/`: Databases, LLM providers, embeddings, loaders, and storage adapters. + - `modules/`: Domain logic (graph, retrieval, ontology, users, processing, observability, etc.). + - `tasks/`: Reusable tasks (e.g., code graph, web scraping, storage). Extend with new tasks here. + - `eval_framework/`: Evaluation utilities and adapters. + - `shared/`: Cross-cutting helpers (logging, settings, utils). + - `tests/`: Unit, integration, CLI, and end-to-end tests organized by feature. + - `__main__.py`: Entrypoint to route to CLI. +- `cognee-mcp/`: Model Context Protocol server exposing cognee as MCP tools (SSE/HTTP/stdio). Contains its own README and Dockerfile. +- `cognee-frontend/`: Next.js UI for local development and demos. +- `distributed/`: Utilities for distributed execution (Modal, workers, queues). +- `examples/`: Example scripts demonstrating the public APIs and features (graph, code graph, multimodal, permissions, etc.). +- `notebooks/`: Jupyter notebooks for demos and tutorials. +- `alembic/`: Database migrations for relational backends. + +Notes: +- Co-locate feature-specific helpers under their respective package (`modules/`, `infrastructure/`, or `tasks/`). +- Extend the system by adding new tasks, loaders, or retrievers rather than modifying core pipeline mechanisms. + +## Build, Test, and Development Commands + +Python (root) – requires Python >= 3.10 and < 3.14. We recommend `uv` for speed and reproducibility. + +- Create/refresh env and install dev deps: +```bash +uv sync --dev --all-extras --reinstall +``` + +- Run the CLI (examples): +```bash +uv run cognee-cli add "Cognee turns documents into AI memory." +uv run cognee-cli cognify +uv run cognee-cli search "What does cognee do?" +uv run cognee-cli -ui # Launches UI, backend API, and MCP server together +``` + +- Start the FastAPI server directly: +```bash +uv run python -m cognee.api.client +``` + +- Run tests (CI mirrors these commands): +```bash +uv run pytest cognee/tests/unit/ -v +uv run pytest cognee/tests/integration/ -v +``` + +- Lint and format (ruff): +```bash +uv run ruff check . +uv run ruff format . +``` + +- Optional static type checks (ty): +```bash +uv run ty check . +``` + +MCP Server (`cognee-mcp/`): + +- Install and run locally: +```bash +cd cognee-mcp +uv sync --dev --all-extras --reinstall +uv run python src/server.py # stdio (default) +uv run python src/server.py --transport sse +uv run python src/server.py --transport http --host 127.0.0.1 --port 8000 --path /mcp +``` + +- API Mode (connect to a running Cognee API): +```bash +uv run python src/server.py --transport sse --api-url http://localhost:8000 --api-token YOUR_TOKEN +``` + +- Docker quickstart (examples): see `cognee-mcp/README.md` for full details +```bash +docker run -e TRANSPORT_MODE=http --env-file ./.env -p 8000:8000 --rm -it cognee/cognee-mcp:main +``` + +Frontend (`cognee-frontend/`): +```bash +cd cognee-frontend +npm install +npm run dev # Next.js dev server +npm run lint # ESLint +npm run build && npm start +``` + +## Coding Style & Naming Conventions + +Python: +- 4-space indentation, modules and functions in `snake_case`, classes in `PascalCase`. +- Public APIs should be type-annotated where practical. Make sure type defined in API signature will be properly displayed in Swagger UI docs. For example this definition: content_type: Optional[str] = Form(default=None) maps to "string" as the default in Swagger docs for content_type, but it should be None/null instead. +- Use `ruff format` before committing; `ruff check` enforces import hygiene and style (line-length 100 configured in `pyproject.toml`). +- Prefer explicit, structured error handling. Use shared logging utilities in `cognee.shared.logging_utils`. + +MCP server and Frontend: +- Follow the local `README.md` and ESLint/TypeScript configuration in `cognee-frontend/`. + +## Testing Guidelines + +- Place Python tests under `cognee/tests/`. + - Unit tests: `cognee/tests/unit/` + - Integration tests: `cognee/tests/integration/` + - CLI tests: `cognee/tests/cli_tests/` +- Name test files `test_*.py`. Use `pytest.mark.asyncio` for async tests. +- Avoid external state; rely on test fixtures and the CI-provided env vars when LLM/embedding providers are required. See CI workflows under `.github/workflows/` for expected environment variables. +- When adding public APIs, provide/update targeted examples under `examples/python/`. + +## Commit & Pull Request Guidelines + +- Use clear, imperative subjects (≤ 72 chars) and conventional commit styling in PR titles. Our CI validates semantic PR titles (see `.github/workflows/pr_lint`). Examples: + - `feat(graph): add temporal edge weighting` + - `fix(api): handle missing auth cookie` + - `docs: update installation instructions` +- Reference related issues/discussions in the PR body and provide brief context. +- PRs should describe scope, list local test commands run, and mention any impacts on MCP server or UI if applicable. +- Sign commits and affirm the DCO (see `CONTRIBUTING.md`). + +## CI Mirrors Local Commands + +Our GitHub Actions run the same ruff checks and pytest suites shown above (`.github/workflows/basic_tests.yml` and related workflows). Use the commands in this document locally to minimize CI surprises. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..edc3e02 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,605 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Cognee is an open-source AI memory platform that transforms raw data into persistent knowledge graphs for AI agents. It replaces traditional RAG (Retrieval-Augmented Generation) with an ECL (Extract, Cognify, Load) pipeline combining vector search, graph databases, and LLM-powered entity extraction. + +**Requirements**: Python 3.10 - 3.14 + +## Development Commands + +### Setup +```bash +# Create virtual environment (recommended: uv) +uv venv && source .venv/bin/activate + +# Install with pip, poetry, or uv +uv pip install -e . + +# Install with dev dependencies +uv pip install -e ".[dev]" + +# Install with specific extras +uv pip install -e ".[postgres,neo4j,docs,chromadb]" + +# Set up pre-commit hooks +pre-commit install +``` + +### Available Installation Extras +- **postgres** / **postgres-binary** - PostgreSQL + PGVector support (also enables the Postgres session-cache backend, `CACHE_BACKEND=postgres`) +- **neo4j** - Neo4j graph database support +- **neptune** - AWS Neptune support +- **chromadb** - ChromaDB vector database +- **docs** - Document processing (unstructured library) +- **scraping** - Web scraping (Tavily, BeautifulSoup, Playwright) +- **langchain** - LangChain integration +- **llama-index** - LlamaIndex integration +- **anthropic** - Anthropic Claude models +- **gemini** - Google Gemini models +- **ollama** - Ollama local models +- **mistral** - Mistral AI models +- **groq** - Groq API support +- **llama-cpp** - Llama.cpp local inference +- **huggingface** - HuggingFace transformers +- **aws** - S3 storage backend +- **redis** - Redis caching +- **graphiti** - Graphiti-core integration +- **baml** - BAML structured output +- **dlt** - Data load tool (dlt) integration +- **docling** - Docling document processing +- **codegraph** - Code graph extraction +- **evals** - Evaluation tools +- **deepeval** - DeepEval testing framework +- **posthog** - PostHog analytics +- **tracing** - OpenTelemetry tracing +- **distributed** - Modal distributed execution +- **dev** - All development tools (pytest, ty, ruff, etc.) +- **debug** - Debugpy for debugging + +### Testing +```bash +# Run all tests +pytest + +# Run with coverage +pytest --cov=cognee --cov-report=html + +# Run specific test file +pytest cognee/tests/test_custom_model.py + +# Run specific test function +pytest cognee/tests/test_custom_model.py::test_function_name + +# Run async tests +pytest -v cognee/tests/integration/ + +# Run unit tests only +pytest cognee/tests/unit/ + +# Run integration tests only +pytest cognee/tests/integration/ +``` + +### Code Quality +```bash +# Run ruff linter +ruff check . + +# Run ruff formatter +ruff format . + +# Run both linting and formatting (pre-commit) +pre-commit run --all-files + +# Type checking with ty +ty check . +``` + +### Running Cognee +```bash +# Using Python SDK +uv run python examples/demos/simple_cognee_example.py + +# Using CLI +cognee-cli add "Your text here" +cognee-cli cognify +cognee-cli search "Your query" +cognee-cli delete --all + +# Launch full stack with UI +cognee-cli -ui +``` + +## Architecture Overview + +### Core Workflow: add → cognify → search/memify + +1. **add()** - Ingest data (files, URLs, text) into datasets +2. **cognify()** - Extract entities/relationships and build knowledge graph +3. **search()** - Query knowledge using various retrieval strategies +4. **memify()** - Enrich graph with additional context and rules + +### Key Architectural Patterns + +#### 1. Pipeline-Based Processing +All data flows through task-based pipelines (`cognee/modules/pipelines/`). Tasks are composable units that can run sequentially or in parallel. Example pipeline tasks: `classify_documents`, `extract_graph_from_data`, `add_data_points`. + +#### 2. Interface-Based Database Adapters +Multiple backends are supported through adapter interfaces: +- **Graph**: Ladybug (default), Neo4j, Neptune, Postgres via `GraphDBInterface` +- **Vector**: LanceDB (default), ChromaDB, PGVector via `VectorDBInterface` +- **Relational**: SQLite (default), PostgreSQL + +Key files: +- `cognee/infrastructure/databases/graph/graph_db_interface.py` +- `cognee/infrastructure/databases/vector/vector_db_interface.py` + +#### 3. Multi-Tenant Access Control +User → Dataset → Data hierarchy with permission-based filtering. Enable with `ENABLE_BACKEND_ACCESS_CONTROL=True`. Each user+dataset combination can have isolated graph/vector databases (when using supported backends: Ladybug, LanceDB, SQLite, Postgres). + +### Layer Structure + +``` +API Layer (cognee/api/v1/) + ↓ +Main Functions (add, cognify, search, memify) + ↓ +Pipeline Orchestrator (cognee/modules/pipelines/) + ↓ +Task Execution Layer (cognee/tasks/) + ↓ +Domain Modules (graph, retrieval, ingestion, etc.) + ↓ +Infrastructure Adapters (LLM, databases) + ↓ +External Services (OpenAI, Ladybug, LanceDB, etc.) +``` + +### Critical Data Flow Paths + +#### ADD: Data Ingestion +`add()` → `resolve_data_directories` → `ingest_data` → `save_data_item_to_storage` → Create Dataset + Data records in relational DB + +Key files: `cognee/api/v1/add/add.py`, `cognee/tasks/ingestion/ingest_data.py` + +#### COGNIFY: Knowledge Graph Construction +`cognify()` → `classify_documents` → `extract_chunks_from_documents` → `extract_graph_from_data` (LLM extracts entities/relationships using Instructor) → `summarize_text` → `add_data_points` (store in graph + vector DBs) + +Key files: +- `cognee/api/v1/cognify/cognify.py` +- `cognee/tasks/graph/extract_graph_from_data.py` +- `cognee/tasks/storage/add_data_points.py` + +#### SEARCH: Retrieval +`search(query_text, query_type)` → route to retriever type → filter by permissions → return results + +Available search types (from `cognee/modules/search/types/SearchType.py`): +- **GRAPH_COMPLETION** (default) - Graph traversal + LLM completion +- **GRAPH_SUMMARY_COMPLETION** - Uses pre-computed summaries with graph context +- **GRAPH_COMPLETION_COT** - Chain-of-thought reasoning over graph +- **GRAPH_COMPLETION_CONTEXT_EXTENSION** - Extended context graph retrieval +- **TRIPLET_COMPLETION** - Triplet-based (subject-predicate-object) search +- **RAG_COMPLETION** - Traditional RAG with chunks +- **CHUNKS** - Vector similarity search over chunks +- **CHUNKS_LEXICAL** - Lexical (keyword) search over chunks +- **SUMMARIES** - Search pre-computed document summaries +- **CYPHER** - Direct Cypher query execution (requires `ALLOW_CYPHER_QUERY=True`) +- **NATURAL_LANGUAGE** - Natural language to structured query +- **TEMPORAL** - Time-aware graph search +- **FEELING_LUCKY** - Automatic search type selection +- **CODING_RULES** - Code-specific search rules + +Key files: +- `cognee/api/v1/search/search.py` +- `cognee/modules/retrieval/context_providers/TripletSearchContextProvider.py` +- `cognee/modules/search/types/SearchType.py` + +### Core Data Models + +#### Engine Models (`cognee/infrastructure/engine/models/`) +- **DataPoint** - Base class for all graph nodes (versioned, with metadata) +- **Edge** - Graph relationships (source, target, relationship type) +- **Triplet** - (Subject, Predicate, Object) representation + +#### Graph Models (`cognee/shared/data_models.py`) +- **KnowledgeGraph** - Container for nodes and edges +- **Node** - Entity (id, name, type, description) +- **Edge** - Relationship (source_node_id, target_node_id, relationship_name) + +### Key Infrastructure Components + +#### LLM Gateway (`cognee/infrastructure/llm/LLMGateway.py`) +Unified interface for multiple LLM providers: OpenAI, Anthropic, Gemini, Ollama, Mistral, Bedrock. Uses Instructor for structured output extraction. + +#### Embedding Engines +Factory pattern for embeddings: `cognee/infrastructure/databases/vector/embeddings/get_embedding_engine.py` + +#### Document Loaders +Support for PDF, DOCX, CSV, images, audio, code files in `cognee/infrastructure/files/` + +## Important Configuration + +### Environment Setup +Copy `.env.template` to `.env` and configure: + +```bash +# Minimal setup (defaults to OpenAI + local file-based databases) +LLM_API_KEY="your_openai_api_key" +LLM_MODEL="openai/gpt-5-mini" # Default model +``` + +**Important**: If you configure only LLM or only embeddings, the other defaults to OpenAI. Ensure you have a working OpenAI API key, or configure both to avoid unexpected defaults. + +Default databases (no extra setup needed): +- **Relational**: SQLite (metadata and state storage) +- **Vector**: LanceDB (embeddings for semantic search) +- **Graph**: Ladybug (knowledge graph and relationships) + +All stored in `.venv` by default. Override with `DATA_ROOT_DIRECTORY` and `SYSTEM_ROOT_DIRECTORY`. + +### Switching Databases + +#### Relational Databases +```bash +# PostgreSQL (requires postgres extra: pip install cognee[postgres]) +DB_PROVIDER=postgres +DB_HOST=localhost +DB_PORT=5432 +DB_USERNAME=cognee +DB_PASSWORD=cognee +DB_NAME=cognee_db +``` + +#### Vector Databases +Supported: lancedb (default), pgvector, chromadb, qdrant, weaviate, milvus +```bash +# ChromaDB (requires chromadb extra) +VECTOR_DB_PROVIDER=chromadb + +# PGVector (requires postgres extra) +VECTOR_DB_PROVIDER=pgvector +VECTOR_DB_URL=postgresql://cognee:cognee@localhost:5432/cognee_db +``` + +#### Graph Databases +Supported: ladybug (default), neo4j, neptune, ladybug-remote, postgres +```bash +# Neo4j (requires neo4j extra: pip install cognee[neo4j]) +GRAPH_DATABASE_PROVIDER=neo4j +GRAPH_DATABASE_URL=bolt://localhost:7687 +GRAPH_DATABASE_NAME=neo4j +GRAPH_DATABASE_USERNAME=neo4j +GRAPH_DATABASE_PASSWORD=yourpassword + +# Remote Ladybug +GRAPH_DATABASE_PROVIDER=ladybug-remote +GRAPH_DATABASE_URL=http://localhost:8000 +GRAPH_DATABASE_USERNAME=your_username +GRAPH_DATABASE_PASSWORD=your_password + +# Postgres (requires postgres extra: pip install cognee[postgres]) +# Does not support raw Cypher queries, natural language search, or Graphiti. +GRAPH_DATABASE_PROVIDER=postgres +GRAPH_DATABASE_URL=postgresql+asyncpg://cognee:cognee@localhost:5432/cognee_db +``` + +#### Session Cache +```bash +# Session/conversation cache backend: sqlite (default), postgres, redis, fs, tapes +CACHE_BACKEND=sqlite +# Optional explicit SQLAlchemy URL for sqlite/postgres cache backends (overrides defaults) +CACHE_DB_URL=postgresql+asyncpg://cognee:cognee@localhost:5432/cognee_db +``` + +### LLM Provider Configuration + +Supported providers: OpenAI (default), Azure OpenAI, Google Gemini, Anthropic, AWS Bedrock, Ollama, LM Studio, Custom (OpenAI-compatible APIs) + +#### OpenAI (Recommended - Minimal Setup) +```bash +LLM_API_KEY="your_openai_api_key" +LLM_MODEL="openai/gpt-5-mini" # default; or gpt-5, gpt-4o, gpt-4o-mini, etc. +LLM_PROVIDER="openai" +``` + +#### Azure OpenAI +```bash +LLM_PROVIDER="azure" +LLM_MODEL="azure/gpt-4o-mini" +LLM_ENDPOINT="https://YOUR-RESOURCE.openai.azure.com/openai/deployments/gpt-4o-mini" +LLM_API_KEY="your_azure_api_key" +LLM_API_VERSION="2024-12-01-preview" +``` + +#### Google Gemini (requires gemini extra) +```bash +LLM_PROVIDER="gemini" +LLM_MODEL="gemini/gemini-2.0-flash-exp" +LLM_API_KEY="your_gemini_api_key" +``` + +#### Anthropic Claude (requires anthropic extra) +```bash +LLM_PROVIDER="anthropic" +LLM_MODEL="claude-3-5-sonnet-20241022" +LLM_API_KEY="your_anthropic_api_key" +``` + +#### Ollama (Local - requires ollama extra) +```bash +LLM_PROVIDER="ollama" +LLM_MODEL="llama3.1:8b" +LLM_ENDPOINT="http://localhost:11434/v1" +LLM_API_KEY="ollama" +EMBEDDING_PROVIDER="ollama" +EMBEDDING_MODEL="nomic-embed-text:latest" +EMBEDDING_ENDPOINT="http://localhost:11434/api/embed" +HUGGINGFACE_TOKENIZER="nomic-ai/nomic-embed-text-v1.5" +``` + +#### Custom / OpenRouter / vLLM +```bash +LLM_PROVIDER="custom" +LLM_MODEL="openrouter/google/gemini-2.0-flash-lite-preview-02-05:free" +LLM_ENDPOINT="https://openrouter.ai/api/v1" +LLM_API_KEY="your_api_key" +``` + +#### AWS Bedrock (requires aws extra) +```bash +LLM_PROVIDER="bedrock" +LLM_MODEL="anthropic.claude-3-sonnet-20240229-v1:0" +AWS_REGION="us-east-1" +AWS_ACCESS_KEY_ID="your_access_key" +AWS_SECRET_ACCESS_KEY="your_secret_key" +# Optional for temporary credentials: +# AWS_SESSION_TOKEN="your_session_token" +``` + +#### LLM Rate Limiting +```bash +LLM_RATE_LIMIT_ENABLED=true +LLM_RATE_LIMIT_REQUESTS=60 # Requests per interval +LLM_RATE_LIMIT_INTERVAL=60 # Interval in seconds +``` + +#### Instructor Mode (Structured Output) +```bash +# LLM_INSTRUCTOR_MODE controls how structured data is extracted +# Each LLM has its own default (e.g., gpt-4o models use "json_schema_mode") +# Override if needed: +LLM_INSTRUCTOR_MODE="json_schema_mode" # or "tool_call", "md_json", etc. +``` + +### Structured Output Framework +```bash +# Use Instructor (default, via litellm) +STRUCTURED_OUTPUT_FRAMEWORK="instructor" + +# Or use BAML (requires baml extra: pip install cognee[baml]) +STRUCTURED_OUTPUT_FRAMEWORK="baml" +BAML_LLM_PROVIDER=openai +BAML_LLM_MODEL="gpt-4o-mini" +BAML_LLM_API_KEY="your_api_key" +``` + +### Storage Backend +```bash +# Local filesystem (default) +STORAGE_BACKEND="local" + +# S3 (requires aws extra: pip install cognee[aws]) +STORAGE_BACKEND="s3" +STORAGE_BUCKET_NAME="your-bucket-name" +AWS_REGION="us-east-1" +AWS_ACCESS_KEY_ID="your_access_key" +AWS_SECRET_ACCESS_KEY="your_secret_key" +DATA_ROOT_DIRECTORY="s3://your-bucket/cognee/data" +SYSTEM_ROOT_DIRECTORY="s3://your-bucket/cognee/system" +``` + +## Extension Points + +### Adding New Functionality + +1. **New Task Type**: Create task function in `cognee/tasks/`, return Task object, register in pipeline +2. **New Database Backend**: Implement `GraphDBInterface` or `VectorDBInterface` in `cognee/infrastructure/databases/` +3. **New LLM Provider**: Add configuration in LLM config (uses litellm) +4. **New Document Processor**: Extend loaders in `cognee/modules/data/processing/` +5. **New Search Type**: Add to `SearchType` enum and implement retriever in `cognee/modules/retrieval/` +6. **Custom Graph Models**: Define Pydantic models extending `DataPoint` in your code + +### Working with Ontologies +Cognee supports ontology-based entity extraction to ground knowledge graphs in standardized semantic frameworks (e.g., OWL ontologies). + +Configuration: +```bash +ONTOLOGY_RESOLVER=rdflib # Default: uses rdflib and OWL files +MATCHING_STRATEGY=fuzzy # Default: fuzzy matching with 80% similarity +ONTOLOGY_FILE_PATH=/path/to/your/ontology.owl # Full path to ontology file +``` + +Implementation: `cognee/modules/ontology/` + +## Branching Strategy + +**IMPORTANT**: Always branch from `dev`, not `main`. The `dev` branch is the active development branch. + +```bash +git checkout dev +git pull origin dev +git checkout -b feature/your-feature-name +``` + +**Core-team PRs must reference a Linear issue.** Put the issue key (e.g. `COG-123`) +in the PR title or the branch name so Linear links the PR to its ticket. This is +enforced by the `Require Linear issue` workflow (`linear-issue-check`), a required +status check. Fork / external-contributor PRs are exempt (the check skips them), so +this rule applies only to internal PRs. + +## Code Style + +- **Formatter**: Ruff (configured in `pyproject.toml`) +- **Line length**: 100 characters +- **String quotes**: Use double quotes `"` not single quotes `'` (enforced by ruff-format) +- **Pre-commit hooks**: Run ruff linting and formatting automatically +- **Type hints**: Encouraged (ty checks enabled) +- **Important**: Always run `pre-commit run --all-files` before committing to catch formatting issues + +## Testing Strategy + +Tests are organized in `cognee/tests/`: +- `unit/` - Unit tests for individual modules +- `integration/` - Full pipeline integration tests +- `cli_tests/` - CLI command tests +- `tasks/` - Task-specific tests + +When adding features, add corresponding tests. Integration tests should cover the full add → cognify → search flow. + +## API Structure + +FastAPI application with versioned routes under `cognee/api/v1/`: +- `/add` - Data ingestion +- `/cognify` - Knowledge graph processing +- `/search` - Query interface +- `/memify` - Graph enrichment +- `/datasets` - Dataset management +- `/users` - Authentication (when `REQUIRE_AUTHENTICATION` is effectively true; see auth posture below) +- `/visualize` - Graph visualization server + +## Python SDK Entry Points + +Main functions exported from `cognee/__init__.py`: +- `add(data, dataset_name)` - Ingest data +- `cognify(datasets)` - Build knowledge graph +- `search(query_text, query_type)` - Query knowledge +- `memify(extraction_tasks, enrichment_tasks)` - Enrich graph +- `delete(data_id)` - Remove data +- `config()` - Configuration management +- `datasets()` - Dataset operations + +All functions are async - use `await` or `asyncio.run()`. + +## Security Considerations + +Several security environment variables in `.env`: +- `ACCEPT_LOCAL_FILE_PATH` - Allow local file paths (default: True) +- `ALLOW_HTTP_REQUESTS` - Allow HTTP requests from Cognee (default: True) +- `ALLOW_CYPHER_QUERY` - Allow raw Cypher queries (default: True) +- `ENABLE_BACKEND_ACCESS_CONTROL` - Multi-tenant isolation (default: True). When `true`, API auth is required and per-user/dataset DB isolation is enabled. When `false`, single-user mode: shared DBs and auth off unless overridden. +- `REQUIRE_AUTHENTICATION` - Explicit auth override. Unset (default): follows `ENABLE_BACKEND_ACCESS_CONTROL`. `false` is ignored when `ENABLE_BACKEND_ACCESS_CONTROL=true`. For a single-user deployment with auth off, set `ENABLE_BACKEND_ACCESS_CONTROL=false` (and optionally `REQUIRE_AUTHENTICATION=false`). + +For production deployments, review and tighten these settings. + +## Common Patterns + +### Creating a Custom Pipeline Task +```python +from cognee.modules.pipelines.tasks.Task import Task + +async def my_custom_task(data): + # Your logic here + processed_data = process(data) + return processed_data + +# Use in pipeline +task = Task(my_custom_task) +``` + +### Accessing Databases Directly +```python +from cognee.infrastructure.databases.graph import get_graph_engine +from cognee.infrastructure.databases.vector import get_vector_engine_async + +graph_engine = await get_graph_engine() +vector_engine = await get_vector_engine_async() +``` + +### Using LLM Gateway +```python +from cognee.infrastructure.llm.get_llm_client import get_llm_client + +llm_client = get_llm_client() +response = await llm_client.acreate_structured_output( + text_input="Your prompt", + system_prompt="System instructions", + response_model=YourPydanticModel +) +``` + +## Key Concepts + +### Datasets +Datasets are project-level containers that support organization, permissions, and isolated processing workflows. Each user can have multiple datasets with different access permissions. + +```python +# Create/use a dataset +await cognee.add(data, dataset_name="my_project") +await cognee.cognify(datasets=["my_project"]) +``` + +### DataPoints +Atomic knowledge units that form the foundation of graph structures. All graph nodes extend the `DataPoint` base class with versioning and metadata support. + +### Permissions System +Multi-tenant architecture with users, roles, and Access Control Lists (ACLs): +- Read, write, delete, and share permissions per dataset +- Enable with `ENABLE_BACKEND_ACCESS_CONTROL=True` +- Supports isolated databases per user+dataset (Ladybug, LanceDB, SQLite, Postgres) + +### Graph Visualization +Launch visualization server: +```bash +# Via CLI +cognee-cli -ui # Launches full stack with UI at http://localhost:3000 + +# Via Python +from cognee.api.v1.visualize import start_visualization_server +await start_visualization_server(port=8080) +``` + +## Debugging & Troubleshooting + +### Debug Configuration +- Set `LITELLM_LOG="DEBUG"` for verbose LLM logs (default: "ERROR") +- Enable debug mode: `ENV="development"` or `ENV="debug"` +- Disable telemetry: `TELEMETRY_DISABLED=1` +- Check logs in structured format (uses structlog) +- Use `debugpy` optional dependency for debugging: `pip install cognee[debug]` + +### Common Issues + +**Ollama + OpenAI Embeddings NoDataError** +- Issue: Mixing Ollama with OpenAI embeddings can cause errors +- Solution: Configure both LLM and embeddings to use the same provider, or ensure `HUGGINGFACE_TOKENIZER` is set when using Ollama + +**LM Studio Structured Output** +- Issue: LM Studio requires explicit instructor mode +- Solution: Set `LLM_INSTRUCTOR_MODE="json_schema_mode"` (or appropriate mode) + +**Default Provider Fallback** +- Issue: Configuring only LLM or only embeddings defaults the other to OpenAI +- Solution: Always configure both LLM and embedding providers, or ensure valid OpenAI API key + +**Permission Denied on Search** +- Behavior: Returns empty list rather than error (prevents information leakage) +- Solution: Check dataset permissions and user access rights + +**Database Connection Issues** +- Check: Verify database URLs, credentials, and that services are running +- Docker users: Use `DB_HOST=host.docker.internal` for local databases + +**Rate Limiting Errors** +- Enable client-side rate limiting: `LLM_RATE_LIMIT_ENABLED=true` +- Adjust limits: `LLM_RATE_LIMIT_REQUESTS` and `LLM_RATE_LIMIT_INTERVAL` + +## Resources + +- [Documentation](https://docs.cognee.ai/) +- [Discord Community](https://discord.gg/NQPKmU5CCg) +- [GitHub Issues](https://github.com/topoteretes/cognee/issues) +- [Example Notebooks](examples/python/) +- [Research Paper](https://arxiv.org/abs/2505.24478) - Optimizing knowledge graphs for LLM reasoning diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..1f1b2ee --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement by emailing at . +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9f26e65 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,163 @@ +> [!IMPORTANT] +> **Note for contributors:** When branching out, create a new branch from the `dev` branch. + +# 🎉 Welcome to **cognee**! + +We're excited that you're interested in contributing to our project! +We want to ensure that every user and contributor feels welcome, included and supported to participate in cognee community. +This guide will help you get started and ensure your contributions can be efficiently integrated into the project. + +## 🌟 Quick Links + +- [Code of Conduct](CODE_OF_CONDUCT.md) +- [Discord Community](https://discord.gg/bcy8xFAtfd) +- [Issue Tracker](https://github.com/topoteretes/cognee/issues) +- [Cognee Docs](https://docs.cognee.ai) + +## 1. 🚀 Ways to Contribute + +You can contribute to **cognee** in many ways: + +- 📝 Submitting bug reports or feature requests +- 💡 Improving documentation +- 🔍 Reviewing pull requests +- 🛠️ Contributing code or tests +- 🌐 Helping other users + +## 📫 Get in Touch + +There are several ways to connect with the **cognee** team and community: + +### GitHub Collaboration +- [Open an issue](https://github.com/topoteretes/cognee/issues) for bug reports, feature requests, or discussions +- Submit pull requests to contribute code or documentation +- Join ongoing discussions in existing issues and PRs + +### Community Channels +- Join our [Discord community](https://discord.gg/bcy8xFAtfd) for real-time discussions +- Participate in community events and discussions +- Get help from other community members + +### Direct Contact +- Email: vasilije@cognee.ai +- For business inquiries or sensitive matters, please reach out via email +- For general questions, prefer public channels like GitHub issues or Discord + +We aim to respond to all communications within 2 business days. For faster responses, consider using our Discord channel where the whole community can help! + +## Issue Labels + +To help you find the most appropriate issues to work on, we use the following labels: + +- `good first issue` - Perfect for newcomers to the project +- `bug` - Something isn't working as expected +- `documentation` - Improvements or additions to documentation +- `enhancement` - New features or improvements +- `help wanted` - Extra attention or assistance needed +- `question` - Further information is requested +- `wontfix` - This will not be worked on + +Looking for a place to start? Try filtering for [good first issues](https://github.com/topoteretes/cognee/labels/good%20first%20issue)! + + +## 2. 🛠️ Development Setup + +### Required tools +* [Python](https://www.python.org/downloads/) +* [uv](https://docs.astral.sh/uv/getting-started/installation/) +* pre-commit: `uv run pip install pre-commit && pre-commit install` + +### Fork and Clone + +1. Fork the [**cognee**](https://github.com/topoteretes/cognee) repository +2. Clone your fork: +```shell +git clone https://github.com//cognee.git +cd cognee +``` +In case you are working on Vector and Graph Adapters +1. Fork the [**cognee-community**](https://github.com/topoteretes/cognee-community) repository +2. Clone your fork: +```shell +git clone https://github.com//cognee-community.git +cd cognee-community +``` + +### Create a Branch + +Create a new branch for your work: +```shell +git checkout -b feature/your-feature-name +``` + +## 3. 🎯 Making Changes + +1. **Code Style**: Follow the project's coding standards +2. **Documentation**: Update relevant documentation +3. **Tests**: Add tests for new features +4. **Commits**: Write clear commit messages + +### Running Tests + +Copy `.env.template` to `.env` and provide your OPENAI_API_KEY as LLM_API_KEY + +```shell +uv run python cognee/tests/test_library.py +``` + +### Running Simple Example + +Copy `.env.template` to `.env` and provide your OPENAI_API_KEY as LLM_API_KEY + +Make sure to run ```shell uv sync ``` in the root cloned folder or set up a virtual environment to run cognee + +```shell +uv run python examples/demos/simple_cognee_example.py +``` + +## 4. 📤 Submitting Changes + +1. Make sure that `pre-commit` and hooks are installed. See `Required tools` section for more information. Try executing `pre-commit run` if you are not sure. +3. Push your changes: +```shell +git add . +git commit -s -m "Description of your changes" +git push origin feature/your-feature-name +``` + +2. Create a Pull Request: + - Go to the [**cognee** repository](https://github.com/topoteretes/cognee) or [cognee community repository](https://github.com/topoteretes/cognee-community) + - Click "Compare & Pull Request" and open a PR against dev branch + - Fill in the PR template with details about your changes + - You MUST provide screenshots of unit and integration tests passing on your machine. We can't merge PRs otherwise + +> **Reviewers are auto-routed.** Cognee uses a [`CODEOWNERS`](.github/CODEOWNERS) +> file to request reviews automatically based on the directories your PR touches. +> No manual ping required — the right person will get notified when you open the PR. + +## 5. 📜 Developer Certificate of Origin (DCO) + +All contributions must be signed-off to indicate agreement with our DCO: + +```shell +git config alias.cos "commit -s" # Create alias for signed commits +``` + +When your PR is ready, please include: +> "I affirm that all code in every commit of this pull request conforms to the terms of the Topoteretes Developer Certificate of Origin" + +## 6. 🤝 Community Guidelines + +- Be respectful and inclusive +- Help others learn and grow +- Follow our [Code of Conduct](CODE_OF_CONDUCT.md) +- Provide constructive feedback +- Ask questions when unsure + +## 7. 📫 Getting Help + +- Open an [issue](https://github.com/topoteretes/cognee/issues) +- Join our Discord community +- Check existing documentation + +Thank you for contributing to **cognee**! 🌟 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..523695a --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,12 @@ +## 💫 Contributors + +Thanks to our amazing contributors! 💖 + + + + + +## 🏆 Top Contributors + +| Rank | Contributor | Contributions | +|------|------------|---------------| diff --git a/DCO.md b/DCO.md new file mode 100644 index 0000000..e2a2401 --- /dev/null +++ b/DCO.md @@ -0,0 +1,17 @@ +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. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4075ccf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,74 @@ +# Use a Python image with uv pre-installed +FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS uv + +# Install the project into `/app` +WORKDIR /app + +# Enable bytecode compilation: without it the venv ships no .pyc files, so +# every container cold start recompiles the entire dependency tree from +# source (measured on cognee-saas-pod: ~8s of a ~13s import, halving startup). +ENV UV_COMPILE_BYTECODE=1 + +# Copy from the cache instead of linking since it's a mounted volume +ENV UV_LINK_MODE=copy + +# Set build argument +ARG DEBUG + +# Set environment variable based on the build argument +ENV DEBUG=${DEBUG} + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + libpq-dev \ + git \ + curl \ + cmake \ + clang \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Copy pyproject.toml and lockfile first for better caching +COPY README.md pyproject.toml uv.lock entrypoint.sh ./ + +# Install the project's dependencies using the lockfile and settings +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --extra debug --extra api --extra postgres --extra neo4j --extra llama-index --extra ollama --extra mistral --extra groq --extra anthropic --frozen --no-install-project --no-dev --no-editable + +# Then, add the rest of the project source code and install it +# Installing separately from its dependencies allows optimal layer caching +COPY ./cognee /app/cognee +COPY ./distributed /app/distributed +COPY ./cognee_db_workers /app/cognee_db_workers +# Compatibility shim that re-exports ladybug under the legacy `kuzu` +# module name. Listed in [tool.hatch.build.targets.wheel] packages, and +# imported at module load by alembic/versions/b9274c27a25a_kuzu_11_migration.py. +COPY ./kuzu /app/kuzu +RUN --mount=type=cache,target=/root/.cache/uv \ +uv sync --extra debug --extra api --extra postgres --extra neo4j --extra llama-index --extra ollama --extra mistral --extra groq --extra anthropic --frozen --no-dev --no-editable + +FROM python:3.12-slim-bookworm + +RUN apt-get update && apt-get install -y \ + libpq5 \ + curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY --from=uv /app /app +# COPY --from=uv /app/.venv /app/.venv +# COPY --from=uv /root/.local /root/.local + +# Strip Windows carriage returns (fixes "no such file" on Windows Docker) +RUN sed -i 's/\r$//' /app/entrypoint.sh && chmod +x /app/entrypoint.sh + +# Place executables in the environment at the front of the path +ENV PATH="/app/.venv/bin:$PATH" + +ENV PYTHONPATH=/app +# ENV LOG_LEVEL=ERROR +ENV PYTHONUNBUFFERED=1 + +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/Dockerfile.ci b/Dockerfile.ci new file mode 100644 index 0000000..9c40ad3 --- /dev/null +++ b/Dockerfile.ci @@ -0,0 +1,26 @@ +FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim +WORKDIR /app +ENV UV_LINK_MODE=copy + +# System deps (superset of all CI needs) +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc g++ libpq-dev git curl cmake clang build-essential \ + postgresql-client libgl1 libglib2.0-0 \ + && rm -rf /var/lib/apt/lists/* + +COPY pyproject.toml uv.lock README.md ./ + +# ALL extras used across CI - no-install-project so project code isn't baked in +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-install-project \ + --extra api --extra docs --extra evals --extra codegraph --extra ollama \ + --extra dev --extra neo4j --extra redis --extra postgres --extra aws \ + --extra baml --extra distributed --extra deepeval --extra scraping \ + --extra notebook --extra docling + +# Marker file for container detection in cognee_setup +RUN echo "test-machine" > /app/.anon_id + +ENV PATH="/app/.venv/bin:$PATH" +ENV PYTHONPATH=/app +ENV PYTHONUNBUFFERED=1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fd57f68 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + 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 2024 Topoteretes UG + + 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. diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..0f264a3 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,10 @@ +topoteretes - cognee +Copyright © 2024 Topoteretes UG. (haftungsbeschränkt), Schonehauser Allee 163, Berlin. + +This project includes software developed at Topoteretes UG. (https://www.cognee.ai/). + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ade3236 --- /dev/null +++ b/README.md @@ -0,0 +1,437 @@ +
+ + Cognee Logo + + +
+ + Cognee - The Open-Source AI Memory Platform for Agents + +

+ Demo + . + Docs + . + Learn More + · + Join Discord + · + Join r/AIMemory + . + Community Plugins & Add-ons +

+ + + [![GitHub forks](https://img.shields.io/github/forks/topoteretes/cognee.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/network/) + [![GitHub stars](https://img.shields.io/github/stars/topoteretes/cognee.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/stargazers/) + [![GitHub commits](https://badgen.net/github/commits/topoteretes/cognee)](https://GitHub.com/topoteretes/cognee/commit/) + [![GitHub tag](https://badgen.net/github/tag/topoteretes/cognee)](https://github.com/topoteretes/cognee/tags/) + [![Downloads](https://static.pepy.tech/badge/cognee)](https://pepy.tech/project/cognee) + [![License](https://img.shields.io/github/license/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/blob/main/LICENSE) + [![Contributors](https://img.shields.io/github/contributors/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/graphs/contributors) + Sponsor + +

+ + topoteretes%2Fcognee | Trendshift + +

+ +Cognee is the open-source AI memory platform that gives AI agents persistent long-term memory across sessions. Ingest data in any format, build a self-hosted knowledge graph, and let every agent recall, connect, and act with full context + +

+ 🌐 This README is also available in: + : + + Deutsch | + Español | + Français | + 日本語 | + 한국어 | + Português | + Русский | + 中文 +

+ +

+ Cognee Demo +

+
+ +📄 Read the research paper: [Optimizing the Interface Between Knowledge Graphs and LLMs for Complex Reasoning](https://arxiv.org/abs/2505.24478) — Markovic et al., 2025 + + +## About Cognee + +Cognee is an open-source AI memory platform for AI Agents. Ingest data in any format, and Cognee continuously builds a self-hosted knowledge graph that gives your agents persistent long-term memory across sessions. Cognee combines vector embeddings, graph reasoning, and cognitive-science-grounded ontology generation to make documents both searchable by meaning and connected by relationships that evolve as your knowledge does. + +:star: _Help us reach more developers and grow the cognee community. Star this repo!_ + +:books: _Check our detailed [documentation](https://docs.cognee.ai/getting-started/installation#environment-configuration) for setup and configuration._ + +:crab: _Available as a plugin for your OpenClaw — [cognee-openclaw](https://www.npmjs.com/package/@cognee/cognee-openclaw)_ + +✴️ _Available as a plugin for your Claude Code — [claude-code-plugin](https://github.com/topoteretes/cognee-integrations/tree/main/integrations/claude-code)_ + +🦀 _Available as a Rust client — [cognee-rs](https://github.com/topoteretes/cognee-rs)_ + +🟦 _Available as a TypeScript client — [@cognee/cognee-ts](https://www.npmjs.com/package/@cognee/cognee-ts)_ + + + +### Why use Cognee: + +- Easily Build Company Brain - unify data from various sources in one place and enable Agents with your domain knowledge +- Knowledge infrastructure — unified ingestion, graph/vector search, runs locally, ontology grounding, multimodal +- Persistent and Learning Agents - learn from feedback, context management, cross-agent knowledge sharing +- Reliable and Trustworthy Agents - agentic user/tenant isolation, traceability, OTEL collector, audit traits + +### How it Works + +

+ Cognee Products +

+ +

+ Cognee Recall +

+ +## Basic Usage & Feature Guide + +To learn more, [check out this short, end-to-end Colab walkthrough](https://colab.research.google.com/drive/1HRrzIvzcbwrESVfX76wJLKmtIg00SUga?usp=sharing) of Cognee's core features. + +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1HRrzIvzcbwrESVfX76wJLKmtIg00SUga?usp=sharing) + +## Quickstart + +Let’s try Cognee in just a few lines of code. + +### Prerequisites + +- Python 3.10 to 3.14 + +### Step 1: Install Cognee + +You can install Cognee with **pip**, **poetry**, **uv**, or your preferred Python package manager. + +```bash +uv pip install cognee +``` + +### Step 2: Configure the LLM +```python +import os +os.environ["LLM_API_KEY"] = "YOUR OPENAI_API_KEY" +``` +Alternatively, create a `.env` file using our [template](https://github.com/topoteretes/cognee/blob/main/.env.template). + +To integrate other LLM providers, see our [LLM Provider Documentation](https://docs.cognee.ai/setup-configuration/llm-providers). + +### Step 3: Run the Pipeline + +Cognee's API gives you four operations — `remember`, `recall`, `forget`, and `improve`: + +```python +import cognee +import asyncio + + +async def main(): + # Store permanently in the knowledge graph (runs add + cognify + improve) + await cognee.remember("Cognee turns documents into AI memory.") + + # Store in session memory (fast cache, syncs to graph in background) + await cognee.remember("User prefers detailed explanations.", session_id="chat_1") + + # Query with auto-routing (picks best search strategy automatically) + results = await cognee.recall("What does Cognee do?") + for result in results: + print(result) + + # Query session memory first, fall through to graph if needed + results = await cognee.recall("What does the user prefer?", session_id="chat_1") + for result in results: + print(result) + + # Delete when done + await cognee.forget(dataset="main_dataset") + + +if __name__ == '__main__': + asyncio.run(main()) + +``` + +### Use the Cognee CLI + +```bash +cognee-cli remember "Cognee turns documents into AI memory." + +cognee-cli recall "What does Cognee do?" + +cognee-cli forget --all +``` + +To open the local UI, run: +```bash +cognee-cli -ui +``` + +> **Note:** The MCP server launched by `cognee-cli -ui` runs inside a Docker container. +> Docker Desktop, Colima, or any OCI-compatible runtime with a working `docker` CLI is +> required. See [Docker & Colima Setup](docs/docker-colima-setup.md) for details. + +## Run with Docker + +Prefer containers? Cognee publishes prebuilt images to Docker Hub on every push to `main`: +[`cognee/cognee`](https://hub.docker.com/r/cognee/cognee) (the API server) and +[`cognee/cognee-mcp`](https://hub.docker.com/r/cognee/cognee-mcp) (the MCP server). + +### Option A — Docker Compose (build from source) + +Clone the repo, create a `.env` with at least `LLM_API_KEY`, then: + +```bash +cp .env.template .env # then edit .env and set LLM_API_KEY + +# Start the API server (http://localhost:8000) +docker compose up + +# Optional profiles (combine as needed): +docker compose --profile ui up # + frontend on http://localhost:3000 +docker compose --profile mcp up # + MCP server on http://localhost:8001 +docker compose --profile postgres up # + Postgres/PGVector +docker compose --profile neo4j up # + Neo4j +``` + +> The `cognee` and `cognee-mcp` services publish different host ports (`8000` vs `8001`), +> so you can run both at once. + +### Option B — Pull the prebuilt image (no clone required) + +```bash +# Create a minimal .env in the current directory +echo 'LLM_API_KEY="YOUR_OPENAI_API_KEY"' > .env + +# API server +docker run --env-file ./.env -p 8000:8000 --rm -it cognee/cognee:main + +# MCP server (HTTP transport) +docker pull cognee/cognee-mcp:main +docker run -e TRANSPORT_MODE=http --env-file ./.env -p 8000:8000 --rm -it cognee/cognee-mcp:main +``` + +See the [MCP server README](cognee-mcp/README.md) for SSE/stdio transports, optional +extras, and MCP client configuration. + +## Use with AI Agents + +### Claude Code + +Install the [Cognee memory plugin](https://github.com/topoteretes/cognee-integrations/tree/main/integrations/claude-code) to give Claude Code persistent memory across sessions. The plugin captures prompts, tool traces, and assistant responses into session memory, injects relevant context on every prompt, and syncs session memory into the permanent knowledge graph at session end. + +**Install** from the Claude Code marketplace. The recommended way is from your shell, *before* launching Claude Code, so the first `claude` launch is a clean session that bootstraps memory automatically: + +```bash +# Add the marketplace and install the plugin (one-time, user-scoped) +claude plugin marketplace add topoteretes/cognee-integrations +claude plugin install cognee-memory@cognee + +# Set env vars for your mode (see below), then launch +export LLM_API_KEY="sk-..." # local mode; or COGNEE_BASE_URL + COGNEE_API_KEY for cloud +claude +``` + +**Local mode** (default) — the plugin bootstraps a local Cognee API at `http://localhost:8011`. Only `LLM_API_KEY` is required; the Cognee API key is auto-minted if absent: + +```bash +export LLM_API_KEY="sk-..." +``` + +**Cognee Cloud or a remote server** — set both: + +```bash +export COGNEE_BASE_URL="https://your-instance.cognee.ai" +export COGNEE_API_KEY="ck_..." +``` + +On startup you should see a "Cognee Memory Connected" system message. + +The plugin hooks into Claude Code's lifecycle — `SessionStart` selects mode and sets up identity, `UserPromptSubmit` injects dataset-scoped context, `PostToolUse` captures tool traces, `Stop` writes the assistant's answer, `PreCompact` preserves memory across context resets, and `SessionEnd` triggers the final sync into the permanent graph. + +See the [plugin README](https://github.com/topoteretes/cognee-integrations/tree/main/integrations/claude-code) for sessions, datasets, and full configuration. + +### Connect to Cognee Cloud + +Point any Python agent at a managed Cognee instance — all SDK calls route to the cloud: + +```python +import cognee + +await cognee.serve(url="https://your-instance.cognee.ai", api_key="ck_...") + +await cognee.remember("important context") +results = await cognee.recall("what happened?") + +await cognee.disconnect() +``` + +## Examples + +Browse more examples in the [`examples/`](examples/) folder — demos, guides, custom pipelines, and database configurations. + +**Use Case 1 — Customer Support Agent** + +```python +Goal: Resolve customer issues using their personal data across finance, support, and product history. + +User: "My invoice looks wrong and the issue is still not resolved." + +Cognee tracks: past interactions, failed actions, resolved cases, product history + +# Agent response: +Agent: "I found 2 similar billing cases resolved last month. + The issue was caused by a sync delay between payment + and invoice systems — a fix was applied on your account." + +# What happens under the hood: +- Unifies data sources from various company channels +- Reconstructs the interaction timeline and tracks outcomes +- Retrieves similar resolved cases +- Maps to the best resolution strategy +- Updates memory after execution so the agent never repeats the same mistake +``` + +**Use Case 2 — Expert Knowledge Distillation (SQL Copilot)** + +```python +Goal: Help junior analysts solve tasks by reusing expert-level queries, patterns, and reasoning. + +User: "How do I calculate customer retention for this dataset?" + +Cognee tracks: expert SQL queries, workflow patterns, schema structures, successful implementations + +# Agent response: +Agent: "Here's how senior analysts solved a similar retention query. + Cognee matched your schema to a known structure and adapted + the expert's logic to fit your dataset." + +# What happens under the hood: +- Extracts and stores patterns from expert SQL queries and workflows +- Maps the current schema to previously seen structures +- Retrieves similar tasks and their successful implementations +- Adapts expert reasoning to the current context +- Updates memory with new successful patterns so junior analysts perform at near-expert level +``` + +## Run the Whole Memory Layer on Postgres + +Graph memory traditionally means operating a stack — a graph database for relationships, a vector database for embeddings, Redis for sessions, and a relational database for metadata — all deployed, secured, and paid for before an agent remembers anything. In cognee 1.0 you can run the entire memory layer on a single Postgres instance. + +| Memory layer | Traditional stack | cognee on Postgres | +| --- | --- | --- | +| Relationships | Neo4j or another graph database | cognee's Postgres graph backend | +| Embeddings | Dedicated vector database | pgvector | +| Sessions | Redis | SQL session-cache backend | +| Metadata | Relational database | same Postgres | + +The graph still exists — it just lives inside the same Postgres-backed memory layer as the text, metadata, and embeddings, so retrieval moves between similarity and structure without crossing service boundaries. In our CI benchmarks, Postgres search ran ~10% faster than the separate graph-plus-vector setup. + +Postgres is the default we recommend for most deployments, but you can still swap in dedicated backends when a workload needs them (Neo4j and Neptune for graphs, Redis for sessions, pgvector and LanceDB for vectors, plus Qdrant, ChromaDB, Weaviate, and Milvus via community adapters). Local development stays fully embedded — SQLite, LanceDB, and Kuzudb — with no extra services to stand up. + +```bash +pip install "cognee[postgres]" +``` + +```bash +DB_PROVIDER=postgres +VECTOR_DB_PROVIDER=pgvector +GRAPH_DATABASE_PROVIDER=postgres +CACHE_BACKEND=postgres + +DB_HOST=localhost +DB_PORT=5432 +DB_USERNAME=cognee +DB_PASSWORD=cognee +DB_NAME=cognee_db +``` + +## Deploy Cognee + +Use [Cognee Cloud](https://www.cognee.ai) for a fully managed experience, or self-host with one of the 1-click deployment configurations below. + +| Platform | Best For | Command | +|----------|----------|---------| +| **Cognee Cloud** | Managed service, no infrastructure to maintain | [Sign up](https://www.cognee.ai) or `await cognee.serve()` | +| **Modal** | Serverless, auto-scaling, GPU workloads | `bash distributed/deploy/modal-deploy.sh` | +| **Railway** | Simplest PaaS, native Postgres | `railway init && railway up` | +| **Fly.io** | Edge deployment, persistent volumes | `bash distributed/deploy/fly-deploy.sh` | +| **Render** | Simple PaaS with managed Postgres | Deploy to Render button | +| **Daytona** | Cloud sandboxes (SDK or CLI) | See `distributed/deploy/daytona_sandbox.py` | + +See the [`distributed/`](distributed/) folder for deploy scripts, worker configurations, and additional details. + +## Use Cognee in Other Languages + +Prefer something other than Python? Cognee also ships official clients for Rust and TypeScript. + +### Getting Started with Rust + +Use the [cognee-rs](https://github.com/topoteretes/cognee-rs) crate to add, cognify, and search from Rust. + +```bash +cargo add cognee +``` + +See the [cognee-rs repository](https://github.com/topoteretes/cognee-rs) for full setup and examples. + +### Getting Started with TypeScript + +Use the [@cognee/cognee-ts](https://www.npmjs.com/package/@cognee/cognee-ts) package to add, cognify, and search from Node.js or the browser. + +```bash +npm install @cognee/cognee-ts +``` + +See the [@cognee/cognee-ts package](https://www.npmjs.com/package/@cognee/cognee-ts) for full setup and examples. + +## Benchmarks + +We ran cognee against [BEAM](https://github.com/topoteretes/cognee), a long-context benchmark that tests whether a system can keep track of a long conversation as it changes — a more useful test for agent memory than typical needle-in-a-haystack benchmarks. Using only cognee's default settings and standard open-source features (no custom models, no BEAM-specific pipelines), we beat the previous state of the art at the 100K-token setting and matched it at 10M tokens. + +| Benchmark | Setting | cognee | Previous SOTA | Obsidian / RAG baseline | +|-----------|---------|--------|---------------|--------------------------| +| BEAM | 100K tokens | **0.79** (>0.8 with per-question routing) | 0.735 | ~0.33 | +| BEAM | 10M tokens | **0.67** | 0.641 | ~0.33 | + +These numbers are a directional signal rather than a definitive measure — see the write-up for the full methodology, caveats, and what the results actually mean. + +## Latest News + +[![Watch Demo](https://img.youtube.com/vi/8hmqS2Y5RVQ/maxresdefault.jpg)](https://www.youtube.com/watch?v=8hmqS2Y5RVQ&t=13s) + + +## Community & Support + +### Contributing +We welcome contributions from the community! Your input helps make Cognee better for everyone. See [`CONTRIBUTING.md`](CONTRIBUTING.md) to get started. + +### Code of Conduct + +We're committed to fostering an inclusive and respectful community. Read our [Code of Conduct](https://github.com/topoteretes/cognee/blob/main/CODE_OF_CONDUCT.md) for guidelines. + +## Research & Citation + +We recently published a research paper on optimizing knowledge graphs for LLM reasoning: + +```bibtex +@misc{markovic2025optimizinginterfaceknowledgegraphs, + title={Optimizing the Interface Between Knowledge Graphs and LLMs for Complex Reasoning}, + author={Vasilije Markovic and Lazar Obradovic and Laszlo Hajdu and Jovan Pavlovic}, + year={2025}, + eprint={2505.24478}, + archivePrefix={arXiv}, + primaryClass={cs.AI}, + url={https://arxiv.org/abs/2505.24478}, +} +``` diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..7534f59 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`topoteretes/cognee` +- 原始仓库:https://github.com/topoteretes/cognee +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/README_ko.md b/README_ko.md new file mode 100644 index 0000000..5728f11 --- /dev/null +++ b/README_ko.md @@ -0,0 +1,220 @@ +
+ + Cognee Logo + + +
+ + Cognee - 정확하고 지속적인 AI 메모리 + +

+ 데모 + . + 문서 + . + 더 알아보기 + · + Discord 참여 + · + r/AIMemory 참여 + . + 커뮤니티 플러그인 & 애드온 +

+ + + [![GitHub forks](https://img.shields.io/github/forks/topoteretes/cognee.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/network/) + [![GitHub stars](https://img.shields.io/github/stars/topoteretes/cognee.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/stargazers/) + [![GitHub commits](https://badgen.net/github/commits/topoteretes/cognee)](https://GitHub.com/topoteretes/cognee/commit/) + [![GitHub tag](https://badgen.net/github/tag/topoteretes/cognee)](https://github.com/topoteretes/cognee/tags/) + [![Downloads](https://static.pepy.tech/badge/cognee)](https://pepy.tech/project/cognee) + [![License](https://img.shields.io/github/license/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/blob/main/LICENSE) + [![Contributors](https://img.shields.io/github/contributors/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/graphs/contributors) + Sponsor + +

+ + cognee - Memory for AI Agents  in 5 lines of code | Product Hunt + + + + topoteretes%2Fcognee | Trendshift + +

+ +데이터를 사용하여 AI 에이전트를 위한 개인화되고 동적인 메모리를 구축하세요. Cognee를 사용하면 RAG를 확장 가능하고 모듈화된 ECL(추출[Extract], 인지화[Cognify], 로드[Load]) 파이프라인으로 대체할 수 있습니다. + +

+ 🌐 사용 가능한 언어 + : + + Deutsch | + Español | + Français | + 日本語 | + 한국어 | + Português | + Русский | + 中文 +

+ + +
+ Why cognee? +
+
+ +## Cognee 소개 + +Cognee는 원시 데이터를 에이전트를 위한 지속적이고 동적인 AI 메모리로 변환하는 오픈 소스 도구이자 플랫폼입니다. 벡터 검색과 그래프 데이터베이스를 결합하여 문서를 의미적으로 검색 가능하게 하고 관계별로 연결합니다. + +Cognee는 두 가지 방식으로 사용할 수 있습니다. + +1. [Cognee 오픈 소스 (셀프 호스팅)](https://docs.cognee.ai/getting-started/installation): 기본적으로 모든 데이터를 로컬에 저장합니다. +2. [Cognee Cloud (관리형)](https://platform.cognee.ai/): 관리형 인프라에서 동일한 OSS 스택을 사용하여 더 쉽게 개발하고 프로덕션화할 수 있습니다. + +### Cognee 오픈 소스 (셀프 호스팅): + +- 과거 대화, 파일, 이미지, 오디오 스크립트 등 모든 유형의 데이터를 상호 연결 +- 기존 RAG 시스템을 그래프와 벡터 기반의 통합 메모리 계층으로 대체 +- 품질과 정밀도를 향상시키면서 개발자 노력과 인프라 비용 절감 +- 30개 이상의 데이터 소스에서 데이터를 수집할 수 있는 Pythonic 데이터 파이프라인 제공 +- 사용자 정의 작업, 모듈식 파이프라인, 내장 검색 엔드포인트를 통한 높은 사용자 정의 가능성 제공 + +### Cognee Cloud (관리형): +- 호스팅된 웹 UI 대시보드 +- 자동 버전 업데이트 +- 리소스 사용량 분석 +- GDPR 준수, 엔터프라이즈급 보안 + +## 기본 사용법 & 기능 가이드 + +자세한 내용은 [Colab 튜토리얼](https://colab.research.google.com/drive/12Vi9zID-M3fpKpKiaqDBvkk98ElkRPWy?usp=sharing)을 확인하세요. + +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/12Vi9zID-M3fpKpKiaqDBvkk98ElkRPWy?usp=sharing) + +## 빠른 시작 + +단 몇 줄의 코드로 Cognee를 사용해 보세요. 자세한 설정 및 구성은 [Cognee 문서](https://docs.cognee.ai/getting-started/installation#environment-configuration)를 참조하세요. + +### 필수 조건 + +- Python 3.10 ~ 3.14 + +### 1단계: Cognee 설치 + +**pip**, **poetry**, **uv** 또는 선호하는 Python 패키지 관리자를 사용하여 Cognee를 설치할 수 있습니다. + +```bash +uv pip install cognee +``` + +### 2단계: LLM 구성 +```python +import os +os.environ["LLM_API_KEY"] = "YOUR OPENAI_API_KEY" +``` +또는 [템플릿](https://github.com/topoteretes/cognee/blob/main/.env.template)을 사용하여 `.env` 파일을 생성하세요. + +다른 LLM 공급자를 통합하려면 [LLM 공급자 문서](https://docs.cognee.ai/setup-configuration/llm-providers)를 참조하세요. + +### 3단계: 파이프라인 실행 + +Cognee는 문서를 가져와 지식 그래프를 생성한 다음 결합된 관계를 기반으로 그래프를 쿼리합니다. + +이제 최소한의 파이프라인을 실행해 보겠습니다. + +```python +import cognee +import asyncio +from pprint import pprint + + +async def main(): + # Cognee에 텍스트 추가 + await cognee.add("Cognee turns documents into AI memory.") + + # 지식 그래프 생성 + await cognee.cognify() + + # 그래프에 메모리 알고리즘 추가 + await cognee.memify() + + # 지식 그래프 쿼리 + results = await cognee.search("What does Cognee do?") + + # 결과 표시 + for result in results: + pprint(result) + + +if __name__ == '__main__': + asyncio.run(main()) + +``` + +보시다시피, 출력은 이전에 Cognee에 저장한 문서에서 생성됩니다. + +```bash + Cognee turns documents into AI memory. +``` + +### Cognee CLI 사용 + +대안으로 다음 필수 명령으로 시작할 수 있습니다. + +```bash +cognee-cli add "Cognee turns documents into AI memory." + +cognee-cli cognify + +cognee-cli search "What does Cognee do?" +cognee-cli delete --all + +``` + +로컬 UI를 열려면 다음을 실행하세요. +```bash +cognee-cli -ui +``` + +## 데모 및 예제 + +Cognee 작동 모습 확인: + +### 지속적인 에이전트 메모리 + +[LangGraph 에이전트를 위한 Cognee 메모리](https://github.com/user-attachments/assets/e113b628-7212-4a2b-b288-0be39a93a1c3) + +### 간단한 GraphRAG + +[데모 보기](https://github.com/user-attachments/assets/f2186b2e-305a-42b0-9c2d-9f4473f15df8) + +### Cognee와 Ollama + +[데모 보기](https://github.com/user-attachments/assets/39672858-f774-4136-b957-1e2de67b8981) + + +## 커뮤니티 및 지원 + +### 기여하기 +여러분들의 기여를 환영합니다! 여러분의 의견은 Cognee를 더 좋게 만드는 데 큰 도움이 됩니다. 시작하려면 [`CONTRIBUTING.md`](CONTRIBUTING.md)를 참조하세요. + +### 규칙 + +우리는 포용적이고 존중하는 커뮤니티를 만들기 위해 노력하고 있습니다. 규칙은 [규칙 문서](https://github.com/topoteretes/cognee/blob/main/CODE_OF_CONDUCT.md)를 확인해주세요. + +## 연구 및 인용 + +최근 LLM 추론을 위한 지식 그래프 최적화에 관한 연구 논문을 발표했습니다. + +```bibtex +@misc{markovic2025optimizinginterfaceknowledgegraphs, + title={Optimizing the Interface Between Knowledge Graphs and LLMs for Complex Reasoning}, + author={Vasilije Markovic and Lazar Obradovic and Laszlo Hajdu and Jovan Pavlovic}, + year={2025}, + eprint={2505.24478}, + archivePrefix={arXiv}, + primaryClass={cs.AI}, + url={https://arxiv.org/abs/2505.24478}, +} +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7086bf5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,6 @@ +# Reporting Security Issues +The Cognee team takes security issues seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. + +To report a security issue, email [info@topoteretes.com](mailto:info@topoteretes.com) and include the word "SECURITY" in the subject line. + +We'll endeavor to respond quickly, and will keep you updated throughout the process. diff --git a/SESSION_POSTGRES_CACHE_PLAN.md b/SESSION_POSTGRES_CACHE_PLAN.md new file mode 100644 index 0000000..cfba851 --- /dev/null +++ b/SESSION_POSTGRES_CACHE_PLAN.md @@ -0,0 +1,336 @@ +# Implementation Plan: Postgres-backed Session Store (CacheDBInterface adapter) + +**Goal:** store cognee session data (QA entries, agent traces, usage logs, and small string KV values) in Postgres via a new `CacheDBInterface` adapter, removing the Redis requirement for session memory. Default backend stays `fs`; Redis and Tapes keep working unchanged. The design keeps a clean seam for a future Turbopuffer backend. + +**Design stance:** minimal-surface adapter that slots into the existing backend plug point (like `FSCacheAdapter`), follows the graph Postgres adapter's engine/schema patterns, and adds **zero new dependencies** (SQLAlchemy is core; asyncpg/psycopg2 ship in the existing `postgres` extra). Where the minimal approach was naive (TTL purging, multi-worker RMW, lock semantics, prune scope), the robust alternatives are grafted in below — each divergence from the minimal design is justified inline. + +--- + +## 1. Overview & goals + +- Add `CACHE_BACKEND=postgres` as a fourth cache backend alongside `redis`, `fs`, `tapes`. +- Implement `PostgresCacheAdapter(CacheDBInterface)` with full behavioral parity to `RedisAdapter`/`FSCacheAdapter` (TTL semantics, merge semantics, error contracts), improving on known Redis races where doing so is observably identical (atomic deletes, `FOR UPDATE` updates). +- Support the formal string KV methods on `CacheDBInterface` (`get_value`/`set_value`/`delete_value`) for small cache values. The old graph-to-session sync contract is removed; new backends should not add an `async_redis` shim for it. +- Non-goals (v1): data migration tooling (cache is 7-day-TTL ephemeral), per-dataset DB isolation (sessions are per-user), distributed `SHARED_LADYBUG_LOCK` support on Postgres (Phase 6), multi-worker `improve()` mutex. + +--- + +## 2. Current state + +**Cache interface** — `cognee/infrastructure/databases/cache/cache_db_interface.py`: ABC with sync lock methods (`acquire_lock`/`release_lock`, concrete `hold_lock()` contextmanager), async QA CRUD (`create_qa_entry`, `get_latest_qa_entries`, `get_all_qa_entries`, `update_qa_entry`, `delete_feedback`, `delete_qa_entry`, `delete_session`), agent traces (`append_agent_trace_step`, `get_agent_trace_session`, `get_agent_trace_feedback`, `get_agent_trace_count`), `prune`, `close`, `log_usage`/`get_usage_logs`, plus concrete back-compat shims `add_qa`/`get_latest_qa`/`get_all_qas` (do not reimplement). Base `__init__(host, port, lock_key="default_lock", log_key="usage_logs")`. Payload models: `SessionQAEntry`/`SessionAgentTraceEntry` in `cognee/infrastructure/databases/cache/models.py` (validators: feedback_score 1–5, `used_graph_element_ids` only `node_ids`/`edge_ids`, `memify_metadata` str→bool, trace sanitization/truncation). + +**Backends** — `cache/redis/RedisAdapter.py` (sync `redis.Redis` for locks + async `redis.asyncio.Redis` for data; one Redis LIST per key `agent_sessions:{user_id}:{session_id}` / `agent_traces:{user_id}:{session_id}` / `{log_key}:{user_id}`; JSON-string elements; `EXPIRE`-on-write sliding TTL; `FLUSHDB` prune); `cache/fscache/FsCacheAdapter.py` (diskcache, whole list as one JSON value under `self.cache`, `cache.transact()`, lock methods raise `SharedLadybugLockRequiresRedisError`); `cache/tapes/TapesCacheAdapter.py` (FS subclass mirroring QA creates over HTTP). + +**Factory** — `cache/get_cache_engine.py`: `@lru_cache`'d `create_cache_engine(...)` branches on `CacheConfig.cache_backend` (`Literal["redis","fs","tapes"]`, default `"fs"`, in `cache/config.py`); returns `None` when `caching` and `usage_logging` are both off; `close_cache_engine()` closes + `cache_clear()`s. **Verified gotcha:** `from ...RedisAdapter import RedisAdapter` executes unconditionally inside the caching-on block, *before* the backend dispatch — it only works because the unused core dep `fakeredis[lua]` transitively installs `redis`. + +**Session manager** — `cognee/infrastructure/session/session_manager.py` (built by `get_session_manager.py`): wraps every interface method, no-ops when `cache_engine is None`. It no longer stores graph snapshots in session prompts. For one release, `delete_session` still deletes the legacy `graph_knowledge:{user_id}:{session_id}` key via `delete_value` so old cache rows do not linger. + +**Locks** — two unrelated systems: (1) `cognee/infrastructure/locks/session_lock.py`: pure in-process asyncio locks (per-`(session_id, op)` dict + improve-lock set), explicitly single-worker scope, **no Redis dependency, no change required**; (2) `CacheDBInterface.acquire_lock`/`release_lock`: **sync** methods used only by the Ladybug graph adapter (`graph/ladybug/adapter.py` ~line 187, via `asyncio.to_thread`) when `SHARED_LADYBUG_LOCK=true` — Redis-only today. + +**Relational sibling** — `cognee/modules/session_lifecycle/models.py` (`session_records`, `session_model_usage`): alembic-managed lifecycle/metrics rows, already Postgres-compatible, untouched by this plan. No FK between cache rows and `session_records` (cache rows TTL-expire; lifecycle rows persist — by design). + +**Template** — `cognee/infrastructure/databases/graph/postgres/adapter.py` + `tables.py`: own `create_async_engine(uri, json_serializer=lambda obj: json.dumps(obj, cls=JSONEncoder), **pool_args)` (verified, lines 40–58), `async_sessionmaker(expire_on_commit=False)`, private `MetaData()` with `create_all(checkfirst=True)` in `initialize()` — not alembic. + +--- + +## 3. Target architecture + +``` +SessionManager / usage_logger / forget / prune_system / memify tasks + │ (unchanged call sites) + ▼ +get_cache_engine() ──reads── CacheConfig (.env: CACHE_BACKEND, CACHE_DB_URL, ...) + │ @lru_cache create_cache_engine() + ├── "redis" → RedisAdapter (unchanged) + ├── "fs" → FSCacheAdapter (unchanged) + ├── "tapes" → TapesCacheAdapter (unchanged) + └── "postgres" → PostgresCacheAdapter (NEW, lazy import) + │ + ├── own async engine (postgresql+asyncpg://...) + │ json_serializer=JSONEncoder (UUID/datetime-safe JSONB) + ├── tables (private MetaData, create-on-init): + │ cache_qa_entries ← agent_sessions:{u}:{s} lists + │ cache_trace_entries← agent_traces:{u}:{s} lists + │ cache_usage_logs ← {log_key}:{u} lists + │ cache_kv ← generic small string KV values + └── get_value/set_value/delete_value over cache_kv +``` + +--- + +## 4. Postgres adapter design + +### 4.1 Module layout + +Mirror the existing backend layout: + +``` +cognee/infrastructure/databases/cache/postgres/ +├── __init__.py # re-export PostgresCacheAdapter +├── tables.py # private MetaData + SQLAlchemy Core tables +└── PostgresCacheAdapter.py # class PostgresCacheAdapter(CacheDBInterface) +``` + +### 4.2 Class skeleton + +```python +class PostgresCacheAdapter(CacheDBInterface): + def __init__( + self, + connection_string: str, # any SQLAlchemy async URL (asyncpg in prod, aiosqlite in tests) + lock_key: str = "default_lock", + log_key: str = "usage_logs", + session_ttl_seconds: int | None = 604800, + agentic_lock_expire: int = 240, # stored now, used by Phase 6 advisory locks + agentic_lock_timeout: int = 300, + purge_interval_seconds: int = 900, + ): + super().__init__(host="", port=0, lock_key=lock_key, log_key=log_key) + self.db_uri = connection_string + self.session_ttl_seconds = session_ttl_seconds + pool_args = dict(get_relational_config().pool_args or {}) + self.engine = create_async_engine( + connection_string, + json_serializer=lambda obj: json.dumps(obj, cls=JSONEncoder), + **pool_args, + ) + self.sessionmaker = async_sessionmaker(bind=self.engine, expire_on_commit=False) + self._initialized = False + self._init_lock = asyncio.Lock() + self._last_purge = 0.0 +``` + +Notes: +- Call `super().__init__` (Redis does; FS doesn't — calling it gives `lock_key`/`log_key`/`lock` attributes). +- Single hashable `connection_string` arg keeps the `@lru_cache`'d factory happy. +- Owning the engine (rather than borrowing `get_relational_engine()`'s) keeps `close()` safe: `close_cache_engine()` → `adapter.close()` → `engine.dispose()` must not kill the shared relational pool. +- Engine-level `json_serializer` with `cognee.modules.storage.utils.JSONEncoder` is load-bearing: without it, asyncpg JSONB inserts containing UUIDs/datetimes fail (graph-adapter gotcha, replicated). +- Connection validation is **lazy** (in `_ensure_initialized`), not eager like Redis's `ping()` — the constructor must stay sync and lru_cache-friendly; the contract only requires `CacheConnectionError` to surface, which it does on first use. Also raise a clear `CacheConnectionError("CACHE_BACKEND=postgres requires cognee[postgres]")` if the asyncpg import fails. + +### 4.3 Table DDL (`cache/postgres/tables.py`) + +Private `MetaData()` — **not** the relational declarative `Base`, **not** alembic-managed (graph-adapter precedent: `graph_node`/`graph_edge` are create-on-init while `session_records` is alembic-managed; private MetaData keeps alembic autogenerate from seeing these). Payload columns use `JSONB().with_variant(JSON(), "sqlite")` so unit tests run on aiosqlite. `cache_`-prefixed names avoid collision with `session_records`/`session_model_usage`. + +```sql +-- QA entries: one row per entry; qa_id promoted to a column for direct UPDATE +-- (Redis buries it in JSON and does linear scan + LSET) +CREATE TABLE cache_qa_entries ( + seq BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- insertion order + user_id TEXT NOT NULL, + session_id TEXT NOT NULL, + qa_id TEXT NOT NULL, + payload JSONB NOT NULL, -- full SessionQAEntry.model_dump() (incl. qa_id, time) + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + expires_at TIMESTAMPTZ NULL, -- NULL = no expiry + UNIQUE (user_id, session_id, qa_id) +); +CREATE INDEX idx_cache_qa_session ON cache_qa_entries (user_id, session_id, seq); +CREATE INDEX idx_cache_qa_expires ON cache_qa_entries (expires_at) WHERE expires_at IS NOT NULL; + +-- Agent traces: append-only +CREATE TABLE cache_trace_entries ( + seq BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + user_id TEXT NOT NULL, + session_id TEXT NOT NULL, + payload JSONB NOT NULL, -- SessionAgentTraceEntry.model_dump() + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + expires_at TIMESTAMPTZ NULL +); +CREATE INDEX idx_cache_trace_session ON cache_trace_entries (user_id, session_id, seq); +CREATE INDEX idx_cache_trace_expires ON cache_trace_entries (expires_at) WHERE expires_at IS NOT NULL; + +-- Usage logs (Redis key {log_key}:{user_id}) +CREATE TABLE cache_usage_logs ( + seq BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + log_key TEXT NOT NULL, -- adapter's self.log_key + user_id TEXT NOT NULL, + payload JSONB NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + expires_at TIMESTAMPTZ NULL +); +CREATE INDEX idx_cache_usage ON cache_usage_logs (log_key, user_id, seq); + +-- String KV for small cache values; legacy graph_knowledge:{u}:{s} keys may be deleted by SessionManager +CREATE TABLE cache_kv ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + expires_at TIMESTAMPTZ NULL +); +``` + +Notes: +- `session_id` stays a **plain string, no FK** to `session_records` (same `session_id` exists under multiple users; cache rows expire while lifecycle rows persist). Keying is always `(user_id, session_id)` — the exact relational analogue of the Redis key prefixes, which also preserves cross-user reads (recall / sessions router resolve the *owner's* `user_id` from `SessionRecord`). +- JSONB, not bytea/pickle: parity with `graph_node.properties` and the Redis JSON-string encoding; queryable for debugging. +- Hot-path: `get_latest_qa_entries` runs on every completion (last 10); `idx_cache_qa_session` makes the tail read an index scan. Rows can be tens of KB (payload embeds `context`) — a later `payload - 'context'` projection optimization is possible, but the contract returns full entries today; don't change behavior. + +### 4.4 Method-by-method implementation + +Every public async method: `await self._ensure_initialized()` first (once-flag under `self._init_lock`; runs `conn.run_sync(cache_metadata.create_all, checkfirst=True)`, wraps first-connect failure in `CacheConnectionError`), then `async with self.sessionmaker() as session, session.begin():`. Error contract (pinned by `test_redis_adapter_crud.py`/`test_fs_adapter_crud.py`): backend/SQLAlchemy errors → wrap in `CacheConnectionError`; create-path *validation* failures → also `CacheConnectionError` (odd but tested); update-path validation → `SessionQAEntryValidationError` **propagated unwrapped**. + +| Method | Implementation | +|---|---| +| `create_qa_entry(user_id, session_id, question, context, answer, qa_id=None, ...)` | `qa_id: str \| None = None` with `str(uuid4())` fallback — match the adapters, not the ABC's `qa_id: str`. Build `SessionQAEntry` (stamps `time=datetime.utcnow().isoformat()`, runs validators; failure → `CacheConnectionError`). `INSERT` `payload=entry.model_dump()`; session-wide TTL refresh (§4.5); scoped lazy purge. One transaction. | +| `get_latest_qa_entries(u, s, last_n=5)` | `SELECT payload WHERE u/s AND not-expired ORDER BY seq DESC LIMIT :n`, reverse in Python → chronological; `SessionQAEntry.model_validate` each. **Return `[]` always on empty, including `last_n==1`** — the Redis `None`-on-`last_n==1` quirk is not replicated; all callers go through `SessionManager` and treat falsy uniformly (FS already returns `[]`). Pin in the unit test. | +| `get_all_qa_entries(u, s)` | Same, `ORDER BY seq ASC`, no LIMIT; `[]` if none. | +| `update_qa_entry(u, s, qa_id, ...)` | One transaction: `SELECT payload ... WHERE user_id/session_id/qa_id ... FOR UPDATE` (`with_for_update()`; no-op on the sqlite test variant, fine). No row → `False`. Merge in Python with exact existing semantics: `None` preserves every field; `memify_metadata = {**existing, **new}` (MERGE not replace — `apply_feedback_weights`/`apply_frequency_weights` idempotency flags depend on it); re-validate via `SessionQAEntry.model_validate`, letting `SessionQAEntryValidationError` propagate; `UPDATE payload`; TTL refresh; `True`. `FOR UPDATE` makes this RMW multi-worker-safe — strictly better than Redis's load/LSET race, observably identical. | +| `delete_feedback(u, s, qa_id)` | Same `FOR UPDATE` pattern; set `feedback_text`/`feedback_score` to `None` in payload; TTL refresh; bool. (This is the only way to clear feedback — `update_qa_entry(feedback_score=None)` preserves.) | +| `delete_qa_entry(u, s, qa_id)` | Single atomic `DELETE ... WHERE user_id/session_id/qa_id` (replaces Redis's non-atomic DEL+RPUSH-loop rewrite — crash-safe improvement); TTL refresh on remaining rows (Redis re-applies TTL after rewrite); return `rowcount > 0`. No "drop key when empty" step — empty session ≡ zero rows. | +| `delete_session(u, s)` | One transaction: `DELETE FROM cache_qa_entries` + `DELETE FROM cache_trace_entries` for `(u, s)`; `True` if either rowcount > 0. (`SessionManager.delete_session` separately deletes the legacy `graph_knowledge:` key via `delete_value` — keep that division for one release.) | +| `append_agent_trace_step(u, s, trace_id, origin_function, status, ...)` | Build `SessionAgentTraceEntry` (`method_params or {}`; model validators do sanitization/truncation for free; blank `trace_id`/`origin_function` → `CacheConnectionError`); INSERT; trace-session TTL refresh. | +| `get_agent_trace_session(u, s, last_n=None)` | `ORDER BY seq ASC`; when `last_n is not None`, `DESC LIMIT` + reverse. Reads do NOT refresh TTL. | +| `get_agent_trace_feedback(u, s, last_n=None)` | `[e.session_feedback for e in await self.get_agent_trace_session(...)]` — exactly the delegation both adapters use. | +| `get_agent_trace_count(u, s)` | `SELECT count(*)` with expiry filter; 0 for missing session. | +| `prune()` | `DELETE FROM` / `TRUNCATE` the **four cache tables only**. Deliberate, documented divergence from Redis `FLUSHDB` (which nukes co-tenant keys and other users' data) — strictly safer; both proposals agree. | +| `close()` | `await self.engine.dispose(close=True)`; dispose the sync lock engine if created (Phase 6); idempotent, swallow + debug-log errors (called by `close_cache_engine` and `prune_data.py`). Reset `self._initialized = False` so a reused instance re-inits. | +| `log_usage(user_id, log_entry, ttl=604800)` | INSERT into `cache_usage_logs` with `log_key=self.log_key`, `expires_at = now()+ttl` if ttl else NULL; **also refresh `expires_at` on the user's existing rows** under the same `(log_key, user_id)` — Redis `EXPIRE`s the whole list, so per-row-only TTL would diverge. | +| `get_usage_logs(user_id, limit=100)` | `SELECT payload WHERE log_key/user_id AND not-expired ORDER BY seq DESC LIMIT :limit` — already most-recent-first (Redis does lrange + reverse). | +| `acquire_lock()` / `release_lock(lock=None)` | **SYNC — do not make async** (Ladybug calls them via `asyncio.to_thread`). **v1: raise `SharedLadybugLockRequiresRedisError`**, imported from `cognee.infrastructure.databases.exceptions.exceptions` directly (it is NOT re-exported in the package `__init__`) — exactly like `FsCacheAdapter`. Inherited `hold_lock()` then raises too — correct. Phase 6 upgrade in §7. | +| Inherited (do not reimplement) | `hold_lock()`, `add_qa`, `get_latest_qa`, `get_all_qas`. | + +**Concurrency posture:** unlike the graph adapter, **no global `asyncio.Lock` serializing writes** — row-level `FOR UPDATE` + single-statement deletes give correct multi-worker behavior with better throughput; the graph adapter's lock exists for bulk upsert batches that don't occur here. Add the graph adapter's tenacity retry on asyncpg `DeadlockDetectedError` as belt-and-braces. + +### 4.5 TTL strategy: `expires_at` + read-time filter + lazy purge + throttled sweep + +Replicates Redis sliding whole-key TTL exactly: + +- **Write refresh:** every mutating op runs, inside its transaction, `UPDATE SET expires_at = now() + :ttl WHERE user_id=:u AND session_id=:s` (skipped when `session_ttl_seconds` is `None`/`<= 0` — `expires_at` stays NULL, expiry disabled). Whole-session refresh matches `EXPIRE session_key`. Sessions are small (tens–hundreds of rows); cheap. +- **Read filter:** every SELECT adds `AND (expires_at IS NULL OR expires_at > now())`. Reads never refresh TTL (tested Redis behavior). Correctness never depends on purging. +- **Purge** ("purge on init only" would leak rows in long-lived processes): (a) scoped `DELETE ... WHERE user_id=:u AND session_id=:s AND expires_at <= now()` opportunistically on writes to that session; (b) a throttled global sweep `DELETE FROM WHERE expires_at <= now()` at most once per `purge_interval_seconds` (default 900) per process, guarded by `pg_try_advisory_lock` on Postgres (skip the guard on the sqlite test variant) so concurrent workers don't stampede. No external cron. Precedents: `FSCacheAdapter.cache.expire()` on init; `session_lifecycle` computing "abandoned" at read time. +- **KV behavior:** string KV values are exact-key values with optional TTL support where the adapter exposes it. No graph-to-session checkpoint keys are written anymore. + +### 4.6 String KV methods + +The old hidden `async_redis` duck-type contract is gone with graph-to-session sync. Implement the formal `CacheDBInterface` string KV methods directly: + +```python +async def get_value(self, key: str) -> str | None: ... +async def set_value(self, key: str, value: str, ttl: int | None = None) -> None: ... +async def delete_value(self, key: str) -> None: ... +``` + +`get_value` returns `str | None`. `set_value` should upsert by key and set `expires_at` when a TTL is supplied. `delete_value` should be idempotent. + +--- + +## 5. Config & provider selection changes + +**`cognee/infrastructure/databases/cache/config.py`:** +- `cache_backend: Literal["redis", "fs", "tapes", "postgres"] = "fs"` (default **unchanged**). +- New fields: `cache_db_url: Optional[str] = None` (env `CACHE_DB_URL`, e.g. `postgresql+asyncpg://cognee:cognee@localhost:5432/cognee_db`) and `cache_purge_interval_seconds: int = 900`. **Single-URL chosen over five discrete `CACHE_DB_*` fields**: one hashable kwarg through the lru_cache'd factory, one config field instead of five, and it matches the adapter's `connection_string` constructor. Do **not** reuse `cache_host`/`cache_port` — they default to Redis's `localhost:6379` (silent misdirection risk). +- Fallback resolution (helper `_resolve_cache_db_url` in `get_cache_engine.py`, mirroring `get_graph_engine.py`'s GRAPH_DATABASE_* → DB_* fallback): `CACHE_DB_URL` if set; else, when `get_relational_config().db_provider == "postgres"`, build `postgresql+asyncpg://DB_USERNAME:DB_PASSWORD@DB_HOST:DB_PORT/DB_NAME` with a warning-level log; else raise `CacheConnectionError("CACHE_BACKEND=postgres requires CACHE_DB_URL or DB_PROVIDER=postgres")`. +- Extend `to_dict()` and the class docstring (both pinned by `cognee/tests/unit/infrastructure/databases/cache/test_cache_config.py::test_cache_config_defaults/test_cache_config_to_dict`, which assert the exact full dict — update them). + +**`cognee/infrastructure/databases/cache/get_cache_engine.py`:** +- Thread two new hashable params through both signatures (same pattern as the `tapes_*` params): `create_cache_engine(..., cache_db_url: str | None = None, cache_purge_interval_seconds: int = 900)`; pass from `get_cache_engine()`. +- New branch with **lazy import** (verified pattern: `TapesCacheAdapter` is imported inside its branch): + +```python +elif config.cache_backend == "postgres": + from cognee.infrastructure.databases.cache.postgres.PostgresCacheAdapter import ( + PostgresCacheAdapter, + ) + return PostgresCacheAdapter( + connection_string=_resolve_cache_db_url(cache_db_url), + lock_key=lock_key, + log_key=log_key, + session_ttl_seconds=session_ttl_seconds, + agentic_lock_expire=agentic_lock_expire, + agentic_lock_timeout=agentic_lock_timeout, + purge_interval_seconds=cache_purge_interval_seconds, + ) +``` + +- Update the `ValueError` message to `"'redis', 'fs', 'tapes', 'postgres'"`. +- Move the currently-unconditional `from ...RedisAdapter import RedisAdapter` (verified: executes for ALL backends once caching is on) inside the `== "redis"` branch — 2-line cleanup that removes the accidental reliance on `fakeredis` pulling in `redis`. Low-risk, do it in this PR. +- lru_cache caveats respected: `cache_db_url` is a hashable string; constructor params frozen after first call until `cache_clear()` (existing behavior); note the existing pool-per-`lock_key` instantiation pattern (Ladybug per-db lock_key) in the adapter docstring. + +**`pyproject.toml`:** no changes — SQLAlchemy is core; asyncpg/psycopg2 are in the existing `postgres` extra. Document "`CACHE_BACKEND=postgres` requires `cognee[postgres]`". + +--- + +## 6. Schema creation / migration approach + +Graph-adapter style, **not alembic**: +- Tables on a private `MetaData()` in `cache/postgres/tables.py`; alembic's `env.py` (`target_metadata = Base.metadata`) never sees them; no migration files. +- Idempotent `create_all(checkfirst=True)` inside `_ensure_initialized()` (once-flag + `asyncio.Lock`), re-run lazily after `close_cache_engine()`/`prune_system`'s `cache_clear()` recreates the adapter — necessary because the cache factory has no `_GraphEngineHandle` equivalent. +- Rationale: cache content is ephemeral/TTL'd, schema is additive, and the repo precedent is explicit (`graph_node`/`graph_edge` create-on-init vs alembic-managed `session_records`). +- **No per-dataset database handler** — do not register anything in `supported_dataset_database_handlers.py` or copy `PostgresGraphDatasetDatabaseHandler`. Sessions are per-user, not per-dataset; isolation parity comes from `(user_id, session_id)` columns. Deployments wanting physical separation point `CACHE_DB_URL` at a dedicated database. + +--- + +## 7. Session lock handling + +- **`cognee/infrastructure/locks/session_lock.py`: no change.** It is pure in-process asyncio with no Redis dependency; its documented single-worker scope is unchanged. Note in the PR description that the adapter's `FOR UPDATE` in `update_qa_entry`/`delete_feedback` makes those RMW paths multi-worker-safe at the storage layer anyway (closes the gap session_lock.py's own docstring flags). +- **`CacheDBInterface.acquire_lock`/`release_lock` (shared Ladybug lock):** + - **v1 (core PR):** raise `SharedLadybugLockRequiresRedisError` — established FS precedent; `SHARED_LADYBUG_LOCK=true + CACHE_BACKEND=postgres` stays unsupported with a clear error. + - **Phase 6 (separate PR):** lazy-create a small **sync** engine (`postgresql+psycopg2`, `pool_size=2`; psycopg2 is already in the `postgres` extra) used only for locks. `lock_id = int.from_bytes(sha256(self.lock_key.encode()).digest()[:8], "big", signed=True)`. `acquire_lock()`: check out a dedicated connection, loop `SELECT pg_try_advisory_lock(:id)` with 0.1 s sleeps until the `agentic_lock_timeout` deadline; on success return a handle object owning the connection; on timeout raise `RuntimeError` (mirrors Redis). `release_lock(lock=None)`: `pg_advisory_unlock` on the **passed** handle, not `self.lock` (explicitly tested Redis behavior); close/return the connection; swallow errors if not held. Handle-bound connections give `thread_local=False` parity (releasable from worker threads). Semantics note to document: advisory locks release on connection death (safer than, but different from, Redis's 240 s `agentic_lock_expire` auto-expiry, which has no direct equivalent). +- Multi-worker `improve()` mutex via `pg_try_advisory_lock` behind `try_acquire_improve_lock`: explicit non-goal / future work. + +--- + +## 8. Test plan + +**Unit CRUD** — new `cognee/tests/unit/infrastructure/databases/cache/test_postgres_adapter_crud.py`, mirroring `test_redis_adapter_crud.py`'s ~25 cases. No real Postgres in pytest (repo convention — Redis suites use a hand-rolled `_InMemoryRedisList` fake; here we get a real-engine equivalent for free): construct `PostgresCacheAdapter("sqlite+aiosqlite:////cache.db")` — the `with_variant(JSON(), "sqlite")` columns and identity-PK→autoincrement degradation make the same code paths run without a server. Cover: +- QA create/get-latest/get-all ordering; uuid4 `qa_id` fallback; `[]` on empty **including `last_n=1`** (pins the FS-style choice) +- update: None-preserves every field; `memify_metadata` MERGE; `SessionQAEntryValidationError` propagates unwrapped; `False` on missing qa_id +- `delete_feedback` nulls both fields; `delete_qa_entry` rowcount semantics + TTL survival; `delete_session` clears both tables +- traces: append/get/count/feedback delegation; sanitization via model validators; blank `trace_id` → `CacheConnectionError` +- TTL: `expires_at` set/refreshed on create/update/delete_feedback/after-delete; NOT refreshed on reads; disabled when `session_ttl_seconds in (0, None)`; expired rows invisible to reads (assert via direct SQL with backdated `expires_at`) +- `prune()` clears only the four cache tables; `close()` idempotent; legacy `add_qa`/`get_latest_qa`/`get_all_qas` shims +- `acquire_lock`/`release_lock` raise `SharedLadybugLockRequiresRedisError` +- KV `get_value`/`set_value`/`delete_value` round-trips, including one legacy `graph_knowledge:{u}:{s}` deletion path through `SessionManager.delete_session` + +**Integration** — add `"postgres"` to the `params=["fs", "redis"]` fixtures in `cognee/tests/integration/infrastructure/session/test_session_sdk_integration.py`, `test_session_persistence_memify_integration.py`, `test_feedback_weights_memify_integration.py` (aiosqlite URL, no server); new `test_session_manager_postgres.py` mirroring `test_session_manager_redis.py` (LLM mocked via `AsyncMock` on `LLMGateway.acreate_structured_output`). + +**Config/factory** — update `test_cache_config.py` exact-dict assertions; new factory tests: `CACHE_BACKEND=postgres` returns `PostgresCacheAdapter`, unknown backend `ValueError` lists all four, URL fallback resolution (CACHE_DB_URL → DB_* → error). + +**CI e2e (real Postgres)** — third twin job `run_conversation_sessions_test_postgres` in `.github/workflows/e2e_tests.yml`, copying `run_conversation_sessions_test_redis` minus the redis service (the pgvector/pg17 service is already provisioned there): env `CACHING=true AUTO_FEEDBACK=true CACHE_BACKEND=postgres DB_PROVIDER=postgres` (relying on the DB_* fallback, which also exercises it) or explicit `CACHE_DB_URL`; extras `"postgres"` only; runs `cognee/tests/test_conversation_history.py` unmodified. Phase 6 adds a `SHARED_LADYBUG_LOCK=true CACHE_BACKEND=postgres` variant of the concurrent-subprocess job running `test_concurrent_subprocess_access.py`, plus real-Postgres-gated advisory-lock unit tests (acquire/release of the *passed* handle, contention timeout → `RuntimeError`, cross-connection mutual exclusion). + +**Regression coverage** — FS/Redis/Postgres tests proving generic string KV round-trips and the legacy `graph_knowledge:` cleanup path work consistently. + +--- + +## 9. Rollout & backward compatibility + +- **No behavior change by default:** `cache_backend` default stays `"fs"`; redis/tapes branches untouched; `get_cache_engine()` still returns `None` when caching + usage_logging are off (every consumer already handles `None`). +- **No data migration tooling:** session cache is 7-day-TTL ephemeral; switching backends starts fresh — same story as redis↔fs today. One-liner in docs. `SessionRecord` rows in the relational DB are unaffected. +- **`forget(everything=True)` / `prune_system` / `prune_data`** paths work unchanged (`prune()`, `close_cache_engine()`, `cache_clear()`); document that Postgres `prune()` is scoped to cognee cache tables (an improvement over `FLUSHDB`). +- **Failure modes:** misconfigured backend raises `CacheConnectionError` (503) at first use; `SHARED_LADYBUG_LOCK=true` + postgres raises `SharedLadybugLockRequiresRedisError` with a clear message until Phase 6. +- **Docs:** `.env.template` "Session cache settings" block (~line 375): add `# CACHE_BACKEND=postgres` + `# CACHE_DB_URL=postgresql+asyncpg://...`; `config.py` docstring backend list; factory `ValueError` message; `CLAUDE.md` extras note ("postgres — also enables the Postgres session-cache backend"); file a task for the external docs.cognee.ai "sessions-and-caching" page. `docker-compose.yml` needs nothing (postgres service exists; redis stays behind its profile). + +--- + +## 10. Phased work breakdown + +| Phase | Scope | Size | +|---|---|---| +| **P1 — Adapter core** | `cache/postgres/tables.py` + `PostgresCacheAdapter.py`: engine, `_ensure_initialized`, QA CRUD (`FOR UPDATE` updates, atomic deletes), TTL refresh + read filter + scoped/throttled purge, prune, close | **L** (~1.5–2 days, ~450 LOC) | +| **P2 — Traces, usage logs, locks-raise, string KV** | `append_agent_trace_step`/trace reads, `log_usage`/`get_usage_logs` (whole-list TTL refresh), lock methods raising `SharedLadybugLockRequiresRedisError`, `get_value`/`set_value`/`delete_value` | **M** (~0.5–1 day, ~150 LOC) | +| **P3 — Wiring** | `config.py` Literal + `cache_db_url` + `cache_purge_interval_seconds` + `to_dict` + docstring; `get_cache_engine.py` branch + `_resolve_cache_db_url` fallback + error msg + RedisAdapter import moved into its branch; config-test fixes | **S** (~0.5 day) | +| **P4 — Tests** | Unit CRUD suite (aiosqlite), `test_session_manager_postgres.py`, `"postgres"` params in 3 integration files, factory/config tests | **M** (~1 day, ~500 LOC tests) | +| **P5 — CI + docs** | `run_conversation_sessions_test_postgres` e2e twin job, `.env.template`, `CLAUDE.md`, docs.cognee.ai task | **S** (~0.5 day) | +| **P6 — Follow-up PR: advisory locks** | psycopg2 `pg_try_advisory_lock` sync lock impl lifting the `SHARED_LADYBUG_LOCK` restriction + concurrent-subprocess CI variant | **M** (~1 day) | + +Core (P1–P5): **~4 days**, one reviewable PR (or two: adapter+tests, then wiring+CI). P6 ships separately. + +--- + +## 11. Future: Turbopuffer adapter sketch (do not build now — verify the seam) + +Turbopuffer is a namespaced object/vector store with upsert-by-id, delete-by-id, and attribute-filtered queries. The same `CacheDBInterface` maps because every cognee cache access is by exact composite key, append-or-point-update, with no cross-key scans: + +- **Namespaces:** today's key strings verbatim (`agent_sessions:{user_id}:{session_id}`, `agent_traces:{...}`, `usage_logs:{user_id}`) — key-prefix tenancy carries over unchanged. +- **Rows:** id = `qa_id` / trace uuid; attributes = entry fields plus a client-stamped monotonic `seq`/`created_at` for ordering (no serial column); vector optional (zero/1-dim placeholder if mandatory — or embed `question+answer` for free semantic recall later, a genuine upside). +- **Operations:** create/append → upsert; `get_latest/get_all` → seq-ordered query (tail = desc + limit + reverse); `update_qa_entry`/`delete_feedback` → read-merge-upsert by id (back to Redis-grade RMW races — no `FOR UPDATE`; acceptable for a cache tier, flag in docs); `delete_session` → namespace delete; `prune()` → enumerate + delete `cognee-*` namespaces by prefix; KV → a `cache_kv` namespace with key-as-id rows; TTL → `expires_at` attribute + query filter + periodic GC (no native TTL — the Postgres TTL design transfers directly); locks → raise `SharedLadybugLockRequiresRedisError` (precedent). +- **Seam requirements this plan satisfies:** (1) all storage details stay behind `CacheDBInterface`; no caller touches adapter internals; (2) entries cross the boundary only as pydantic `model_dump()` payloads; (3) ordering is adapter-internal (`seq` column vs `seq` attribute) — nothing leaks; do not let SQL row ids escape into return values during P2; (4) wiring is mechanical: `cache_backend` Literal += `"turbopuffer"`, lazy-import elif, `TURBOPUFFER_API_KEY`/`TURBOPUFFER_REGION` threaded through the lru_cache'd factory. + +--- + +## 12. Open questions + +1. **Default fallback to the relational DB:** when `CACHE_DB_URL` is unset and `DB_PROVIDER=postgres`, the cache silently shares the relational database (distinct `cache_*` tables, warning logged). Is co-tenancy acceptable as the default, or should `CACHE_DB_URL` be mandatory? +2. **Normalize the Redis `None`-on-`last_n==1` quirk in `RedisAdapter` itself** (return `[]` like FS/Postgres), or leave Redis as-is and only document Postgres's `[]` choice? (Some tests pin the Redis behavior.) +3. **Hot-path payload size:** should `get_latest_qa_entries` eventually project `payload - 'context'` for history reads (formatted history uses `include_context=False` but still fetches full entries)? Behavior-preserving today; revisit with real latency data. +4. **Throttled global purge tuning:** is 900 s / per-process advisory-lock-guarded sweep enough for high-volume deployments, or is a documented external cron (`DELETE ... WHERE expires_at <= now()`) preferable at scale? +5. **`fakeredis[lua]` core dependency:** verified unused in-repo and only load-bearing because it transitively installs `redis` for the unconditional import this plan removes — candidate for deletion in a separate cleanup PR (needs a check that no downstream consumers rely on it). +6. **Lock auto-expiry parity (Phase 6):** Redis locks auto-expire after `agentic_lock_expire=240` s; pg advisory locks hold until connection death. Is connection-scoped release acceptable, or do we need a watchdog that closes the lock connection after 240 s? diff --git a/assets/cognee-demo.gif b/assets/cognee-demo.gif new file mode 100644 index 0000000..092bc09 Binary files /dev/null and b/assets/cognee-demo.gif differ diff --git a/assets/cognee-logo-transparent.png b/assets/cognee-logo-transparent.png new file mode 100644 index 0000000..348ff1f Binary files /dev/null and b/assets/cognee-logo-transparent.png differ diff --git a/assets/cognee-new-ui.webp b/assets/cognee-new-ui.webp new file mode 100644 index 0000000..81e408d Binary files /dev/null and b/assets/cognee-new-ui.webp differ diff --git a/assets/cognee-paper.png b/assets/cognee-paper.png new file mode 100644 index 0000000..df7113b Binary files /dev/null and b/assets/cognee-paper.png differ diff --git a/assets/cognee-ui-1.webp b/assets/cognee-ui-1.webp new file mode 100644 index 0000000..714bf3a Binary files /dev/null and b/assets/cognee-ui-1.webp differ diff --git a/assets/cognee-ui-2.webp b/assets/cognee-ui-2.webp new file mode 100644 index 0000000..840470c Binary files /dev/null and b/assets/cognee-ui-2.webp differ diff --git a/assets/cognee_benefits.png b/assets/cognee_benefits.png new file mode 100644 index 0000000..0150b09 Binary files /dev/null and b/assets/cognee_benefits.png differ diff --git a/assets/cognee_comparison.png b/assets/cognee_comparison.png new file mode 100644 index 0000000..60fb65f Binary files /dev/null and b/assets/cognee_comparison.png differ diff --git a/assets/cognee_demo.gif b/assets/cognee_demo.gif new file mode 100644 index 0000000..56f4ce7 Binary files /dev/null and b/assets/cognee_demo.gif differ diff --git a/assets/cognee_diagram.png b/assets/cognee_diagram.png new file mode 100644 index 0000000..fe847f0 Binary files /dev/null and b/assets/cognee_diagram.png differ diff --git a/assets/cognee_logo.png b/assets/cognee_logo.png new file mode 100644 index 0000000..744ba20 Binary files /dev/null and b/assets/cognee_logo.png differ diff --git a/assets/cognee_products.png b/assets/cognee_products.png new file mode 100644 index 0000000..c892394 Binary files /dev/null and b/assets/cognee_products.png differ diff --git a/assets/community/README.pt.md b/assets/community/README.pt.md new file mode 100644 index 0000000..3a089e3 --- /dev/null +++ b/assets/community/README.pt.md @@ -0,0 +1,153 @@ +
+ + Cognee Logo + + +
+ + cognee - Memória para Agentes de IA em 5 linhas de código + +

+ Demonstração + . + Saiba mais + · + Participe do Discord +

+ + + + [![GitHub forks](https://img.shields.io/github/forks/topoteretes/cognee.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/network/) + [![GitHub stars](https://img.shields.io/github/stars/topoteretes/cognee.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/stargazers/) + [![GitHub commits](https://badgen.net/github/commits/topoteretes/cognee)](https://GitHub.com/topoteretes/cognee/commit/) + [![Github tag](https://badgen.net/github/tag/topoteretes/cognee)](https://github.com/topoteretes/cognee/tags/) + [![Downloads](https://static.pepy.tech/badge/cognee)](https://pepy.tech/project/cognee) + [![License](https://img.shields.io/github/license/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/blob/main/LICENSE) + [![Contributors](https://img.shields.io/github/contributors/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/graphs/contributors) + +cognee - Memory for AI Agents  in 5 lines of code | Product Hunt + + + +Crie uma memória dinâmica para Agentes usando pipelines ECL (Extrair, Cognificar, Carregar) escaláveis e modulares. + +Saiba mais sobre os [casos de uso](https://docs.cognee.ai/use-cases) e [avaliações](https://github.com/topoteretes/cognee/tree/main/evals) + +
+ Por que cognee? +
+ +
+ + + +## Funcionalidades + +- Conecte e recupere suas conversas passadas, documentos, imagens e transcrições de áudio +- Reduza alucinações, esforço de desenvolvimento e custos +- Carregue dados em bancos de dados de grafos e vetores usando apenas Pydantic +- Transforme e organize seus dados enquanto os coleta de mais de 30 fontes diferentes + +## Primeiros Passos + +Dê os primeiros passos com facilidade usando um Google Colab notebook ou um repositório inicial + +## Contribuindo + +Suas contribuições estão no centro de tornar este um verdadeiro projeto open source. Qualquer contribuição que você fizer será **muito bem-vinda**. Veja o [`CONTRIBUTING.md`](/CONTRIBUTING.md) para mais informações. +## 📦 Instalação + +Você pode instalar o Cognee usando **pip**, **poetry**, **uv** ou qualquer outro gerenciador de pacotes Python. + +### Com pip + +```bash +pip install cognee +``` + +## 💻 Uso Básico + +### Configuração + +```python +import os +os.environ["LLM_API_KEY"] = "SUA_OPENAI_API_KEY" +``` + +Você também pode definir as variáveis criando um arquivo .env, usando o nosso modelo. +Para usar diferentes provedores de LLM, consulte nossa documentação . + +### Exemplo simples + +Este script executará o pipeline *default*: + +```python +import cognee +import asyncio + + +async def main(): + # Adiciona texto ao cognee + await cognee.add("Processamento de linguagem natural (NLP) é um subcampo interdisciplinar da ciência da computação e recuperação de informações.") + + # Gera o grafo de conhecimento + await cognee.cognify() + + # Consulta o grafo de conhecimento + results = await cognee.search("Me fale sobre NLP") + + # Exibe os resultados + for result in results: + print(result) + + +if __name__ == '__main__': + asyncio.run(main()) + +``` +Exemplo do output: +``` + O Processamento de Linguagem Natural (NLP) é um campo interdisciplinar e transdisciplinar que envolve ciência da computação e recuperação de informações. Ele se concentra na interação entre computadores e a linguagem humana, permitindo que as máquinas compreendam e processem a linguagem natural. + +``` + +Visualização do grafo: +Visualização do Grafo +Abra no [navegador](https://rawcdn.githack.com/topoteretes/cognee/refs/heads/main/assets/graph_visualization.html). + + +Para um uso mais avançado, confira nossa documentação. + + +## Entenda nossa arquitetura + +
+ diagrama conceitual do cognee +
+ +## Demonstrações + +1. O que é memória de IA: +[Saiba mais sobre o cognee](https://github.com/user-attachments/assets/8b2a0050-5ec4-424c-b417-8269971503f0) + +2. Demonstração simples do GraphRAG + +[Demonstração simples do GraphRAG](https://github.com/user-attachments/assets/d80b0776-4eb9-4b8e-aa22-3691e2d44b8f) + +3. Cognee com Ollama + +[cognee com modelos locais](https://github.com/user-attachments/assets/8621d3e8-ecb8-4860-afb2-5594f2ee17db) + +## Código de Conduta + +Estamos comprometidos em tornar o open source uma experiência agradável e respeitosa para nossa comunidade. Veja o CODE_OF_CONDUCT para mais informações. + +## 💫 Contribuidores + + + contribuidores + + +## Histórico de Estrelas + +[![Gráfico de Histórico de Estrelas](https://api.star-history.com/svg?repos=topoteretes/cognee&type=Date)](https://star-history.com/#topoteretes/cognee&Date) diff --git a/assets/community/README.ru.md b/assets/community/README.ru.md new file mode 100644 index 0000000..8e7a984 --- /dev/null +++ b/assets/community/README.ru.md @@ -0,0 +1,159 @@ +
+ + Cognee Logo + + +
+ +Cognee - это платформа для управления памятью ИИ, предназначенная для повышения точности и надежности ответов больших языковых моделей (LLM) и ИИ-агентов. + +

+ Демо + · + Узнать больше + · + Присоединиться к Discord +

+ + + + [![GitHub forks](https://img.shields.io/github/forks/topoteretes/cognee.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/network/) + [![GitHub stars](https://img.shields.io/github/stars/topoteretes/cognee.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/stargazers/) + [![GitHub commits](https://badgen.net/github/commits/topoteretes/cognee)](https://GitHub.com/topoteretes/cognee/commit/) + [![Github tag](https://badgen.net/github/tag/topoteretes/cognee)](https://github.com/topoteretes/cognee/tags/) + [![Downloads](https://static.pepy.tech/badge/cognee)](https://pepy.tech/project/cognee) + [![License](https://img.shields.io/github/license/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/blob/main/LICENSE) + [![Contributors](https://img.shields.io/github/contributors/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/graphs/contributors) + +cognee - Memory for AI Agents  in 5 lines of code | Product Hunt + + +Создавай динамическую память для агентов, используя ECL (Extract -> Cognify -> Load) конвейер. + +Узнайте больше о [вариантах использования](https://docs.cognee.ai/use-cases) и [бенчмарках](https://github.com/topoteretes/cognee/tree/main/evals) + +
+ Почему cognee? +
+ +
+ + + +## Функциональность + +- Интеграция и извлечение данных: Позволяет подключать и извлекать прошлые разговоры, документы, изображения и аудиозаписи, обеспечивая доступ к разнообразным источникам информации. + +- Снижение галлюцинаций и затрат: Уменьшает вероятность генерации недостоверных ответов, снижает затраты на разработку и эксплуатацию ИИ-приложений. + +- Загрузка данных с использованием Pydantic: Обеспечивает загрузку данных в графовые и векторные базы данных с использованием только Pydantic, упрощая процесс интеграции. + +- Трансформация и организация данных: Позволяет трансформировать и структурировать данные, собирая их из более чем 30 различных источников, включая PDF, таблицы и другие форматы. + +- Модульные ECL-пайплайны: Использует модульные пайплайны Extract, Cognify, Load (ECL) для обработки данных, что обеспечивает гибкость и масштабируемость системы. + +- Поддержка онтологий на основе RDF: Использует онтологии на основе RDF для более интеллектуального управления данными и улучшения семантического понимания. + +- Локальное развертывание и масштабируемость: Позволяет развернуть систему на собственных серверах, обеспечивая безопасность данных и соответствие требованиям конфиденциальности. Система масштабируется для обработки больших объемов данных. + +## Начало работы + +Начните легко с помощью Google Colab блокнота или стартового репозитория + +## Помощь проекту + +Ваш вклад является основой для превращения этого в настоящий проект с открытым исходным кодом. Любой вклад, который вы сделаете, будет **очень приветствоваться**. Смотрите [`CONTRIBUTING.md`](/CONTRIBUTING.md) для получения дополнительной информации. + +## 📦 Установка + +Вы можете установить Cognee, используя **pip**, **poetry**, **uv** или любой другой менеджер пакетов Python. + +### С помощью pip + +```bash +pip install cognee +``` + +## 💻 Базовое использование + +### Настройка + +```python +import os +os.environ["LLM_API_KEY"] = "ВАШ_OPENAI_API_KEY" +``` + +Вы также можете установить переменные, создав файл .env, используя наш шаблон. +Для использования различных провайдеров LLM смотрите нашу документацию. + +### Пример использования + +Этот скрипт выполнит *стандартный* конвейер: + +```python +import cognee +import asyncio + + +async def main(): + # Добавляем текст в cognee + await cognee.add("Обработка естественного языка (NLP) - это междисциплинарная область компьютерных наук и информационного поиска.") + + # Генерируем граф знаний + await cognee.cognify() + + # Делаем поиск + results = await cognee.search("Расскажите мне о NLP") + + # Отображаем результаты + for result in results: + print(result) + + +if __name__ == '__main__': + asyncio.run(main()) + +``` +Пример вывода: +``` +Обработка естественного языка (NLP) — это междисциплинарная область, которая объединяет компьютерные науки и информационный поиск. Она включает в себя технологии и методы обработки человеческого языка для создания интерфейсов и обработки данных. +``` + +Визуализация графа: +Визуализация графа +[Открыть пример графа в браузере](https://rawcdn.githack.com/topoteretes/cognee/refs/heads/main/assets/graph_visualization.html). + + +Больше примеров в документации. + + +## Изучите нашу архитектуру + +
+ концептуальная диаграмма cognee +
+ +## Демонстрации + +1. Что такое память ИИ: +[Видео](https://github.com/user-attachments/assets/8b2a0050-5ec4-424c-b417-8269971503f0) + +2. Простая демонстрация GraphRAG +[Видео](https://github.com/user-attachments/assets/d80b0776-4eb9-4b8e-aa22-3691e2d44b8f) + +3. Cognee с Ollama +[Видео](https://github.com/user-attachments/assets/8621d3e8-ecb8-4860-afb2-5594f2ee17db) + +## Правила поведения + +Мы стремимся сделать открытый исходный код приятным и уважительным опытом для нашего сообщества. Смотрите CODE_OF_CONDUCT для получения дополнительной информации. + +## 💫 Контрибьюторы + + + участники + + +## История звёзд на GitHub + +[![График истории звёзд](https://api.star-history.com/svg?repos=topoteretes/cognee&type=Date)](https://star-history.com/#topoteretes/cognee&Date) diff --git a/assets/community/README.zh.md b/assets/community/README.zh.md new file mode 100644 index 0000000..e002b1f --- /dev/null +++ b/assets/community/README.zh.md @@ -0,0 +1,162 @@ +
+ + Cognee Logo + + +
+ + cognee - AI应用和智能体的记忆层 + +

+ 演示 + . + 了解更多 + · + 加入Discord +

+ + + [![GitHub forks](https://img.shields.io/github/forks/topoteretes/cognee.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/network/) + [![GitHub stars](https://img.shields.io/github/stars/topoteretes/cognee.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/topoteretes/cognee/stargazers/) + [![GitHub commits](https://badgen.net/github/commits/topoteretes/cognee)](https://GitHub.com/topoteretes/cognee/commit/) + [![Github tag](https://badgen.net/github/tag/topoteretes/cognee)](https://github.com/topoteretes/cognee/tags/) + [![Downloads](https://static.pepy.tech/badge/cognee)](https://pepy.tech/project/cognee) + [![License](https://img.shields.io/github/license/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/blob/main/LICENSE) + [![Contributors](https://img.shields.io/github/contributors/topoteretes/cognee?colorA=00C586&colorB=000000)](https://github.com/topoteretes/cognee/graphs/contributors) + + 可靠的AI智能体响应。 + + + +使用可扩展、模块化的ECL(提取、认知、加载)管道构建动态智能体记忆。 + +更多[使用场景](https://docs.cognee.ai/use_cases)。 + +
+ 为什么选择cognee? +
+ +
+ + + + +## 功能特性 + +- 互联并检索您的历史对话、文档、图像和音频转录 +- 减少幻觉、开发人员工作量和成本 +- 仅使用Pydantic将数据加载到图形和向量数据库 +- 从30多个数据源摄取数据时进行数据操作 + +## 开始使用 + +通过Google Colab 笔记本入门项目快速上手 + +## 贡献 +您的贡献是使这成为真正开源项目的核心。我们**非常感谢**任何贡献。更多信息请参阅[`CONTRIBUTING.md`](CONTRIBUTING.md)。 + + + + + +## 📦 安装 + +您可以使用**pip**、**poetry**、**uv**或任何其他Python包管理器安装Cognee。 + +### 使用pip + +```bash +pip install cognee +``` + +## 💻 基本用法 + +### 设置 + +``` +import os +os.environ["LLM_API_KEY"] = "YOUR OPENAI_API_KEY" + +``` + +您也可以通过创建.env文件设置变量,使用我们的模板。 +要使用不同的LLM提供商,请查看我们的文档获取更多信息。 + + +### 简单示例 + +此脚本将运行默认管道: + +```python +import cognee +import asyncio + + +async def main(): + # Add text to cognee + await cognee.add("自然语言处理(NLP)是计算机科学和信息检索的跨学科领域。") + + # Generate the knowledge graph + await cognee.cognify() + + # Query the knowledge graph + results = await cognee.search("告诉我关于NLP") + + # Display the results + for result in results: + print(result) + + +if __name__ == '__main__': + asyncio.run(main()) + +``` +示例输出: +``` + 自然语言处理(NLP)是计算机科学和信息检索的跨学科领域。它关注计算机和人类语言之间的交互,使机器能够理解和处理自然语言。 + +``` +图形可视化: +图形可视化 +在[浏览器](https://rawcdn.githack.com/topoteretes/cognee/refs/heads/main/assets/graph_visualization.html)中打开。 + +有关更高级的用法,请查看我们的文档。 + + +## 了解我们的架构 + +
+ cognee概念图 +
+ + + +## 演示 + +1. 什么是AI记忆: + +[了解cognee](https://github.com/user-attachments/assets/8b2a0050-5ec4-424c-b417-8269971503f0) + +2. 简单GraphRAG演示 + +[简单GraphRAG演示](https://github.com/user-attachments/assets/f57fd9ea-1dc0-4904-86eb-de78519fdc32) + +3. cognee与Ollama + +[cognee与本地模型](https://github.com/user-attachments/assets/834baf9a-c371-4ecf-92dd-e144bd0eb3f6) + + +## 行为准则 + +我们致力于为我们的社区提供愉快和尊重的开源体验。有关更多信息,请参阅CODE_OF_CONDUCT。 + +## 💫 贡献者 + + + contributors + + + +## Star历史 + +[![Star History Chart](https://api.star-history.com/svg?repos=topoteretes/cognee&type=Date)](https://star-history.com/#topoteretes/cognee&Date) diff --git a/assets/community/cognee_benefits_zh.JPG b/assets/community/cognee_benefits_zh.JPG new file mode 100644 index 0000000..30474b8 Binary files /dev/null and b/assets/community/cognee_benefits_zh.JPG differ diff --git a/assets/community/cognee_diagram_zh.JPG b/assets/community/cognee_diagram_zh.JPG new file mode 100644 index 0000000..df78ec6 Binary files /dev/null and b/assets/community/cognee_diagram_zh.JPG differ diff --git a/assets/community/graph_visualization_pt.png b/assets/community/graph_visualization_pt.png new file mode 100644 index 0000000..95d4aba Binary files /dev/null and b/assets/community/graph_visualization_pt.png differ diff --git a/assets/community/graph_visualization_ru.png b/assets/community/graph_visualization_ru.png new file mode 100644 index 0000000..3d1a6b4 Binary files /dev/null and b/assets/community/graph_visualization_ru.png differ diff --git a/assets/competitor_comparison.png b/assets/competitor_comparison.png new file mode 100644 index 0000000..6da2801 Binary files /dev/null and b/assets/competitor_comparison.png differ diff --git a/assets/infographic_final.png b/assets/infographic_final.png new file mode 100644 index 0000000..c43f363 Binary files /dev/null and b/assets/infographic_final.png differ diff --git a/assets/memory-graph.png b/assets/memory-graph.png new file mode 100644 index 0000000..22d63bf Binary files /dev/null and b/assets/memory-graph.png differ diff --git a/assets/recall.svg b/assets/recall.svg new file mode 100644 index 0000000..32f6f4f --- /dev/null +++ b/assets/recall.svg @@ -0,0 +1,70 @@ + +recall() answering a question from the memory graph +cognee.recall() reads "Where is cognee based?", lights up the path from the cognee node along the "based in" edge to Berlin, and returns the grounded answer "cognee is based in Berlin". + + + + + + + +Ask a question and get answers grounded in your graph + + + + + +cognee.recall() + + + + +Where +is +cognee +based? + + + + + + + + + + +existing memory + + + + + + + + + +based in + + +Vasilije + + + +cognee + + + +Berlin + + + + + + + + + + +cognee is based in Berlin + + diff --git a/assets/remember.svg b/assets/remember.svg new file mode 100644 index 0000000..c9e87b3 --- /dev/null +++ b/assets/remember.svg @@ -0,0 +1,69 @@ + +remember() turning a sentence into a memory graph +cognee.remember() reads "Vasilije founded cognee in Berlin", highlights three entities, which sprout as glowing purple nodes connected by labeled edges and link into existing memory. + + + + + + + +Turn a sentence into a memory graph agents can reason over + + + + + +cognee.remember() + + + + + + +Vasilije +founded +cognee +in +Berlin + + + + + + + + + + +existing memory + + + + + + + + + + + + + + +founded + +based in + + + +Vasilije + + + +cognee + + + +Berlin + diff --git a/bin/dockerize b/bin/dockerize new file mode 100755 index 0000000..ab210e3 --- /dev/null +++ b/bin/dockerize @@ -0,0 +1,38 @@ +set -euo pipefail + +AWS_REGION=${region:-eu-west-1} +AWS_DEPLOYMENT_ACCOUNT=${account:-463722570299} +AWS_REPOSITORY=${repo:-"${AWS_DEPLOYMENT_ACCOUNT}.dkr.ecr.${AWS_REGION}.amazonaws.com"} + +STAGE=${stage:-"dev"} +SHA_SHORT="$(git rev-parse --short HEAD)" +CUR_DATE="$(date +%Y%m%d%H%M%S)" +VERSION="$STAGE-$CUR_DATE-$SHA_SHORT" +IMAGE_NAME=${image_name:-cognee-${STAGE}} + +REPO_NAME="${AWS_REPOSITORY}/${IMAGE_NAME}" +FULL_IMAGE_NAME="${REPO_NAME}" +APP_DIR=${app_dir:-"."} + +# ECHO "$FULL_IMAGE_NAME:latest" + +PUBLISH=${publish:-false} + +echo "Building docker image ${FULL_IMAGE_NAME} located in dir ${app_dir}" + +pushd "${APP_DIR}" && + docker buildx build --platform linux/amd64 \ + --build-arg STAGE=${STAGE} \ + -t "${FULL_IMAGE_NAME}:latest" -t "${FULL_IMAGE_NAME}:${VERSION}" . && + echo "${VERSION}" >/tmp/.DOCKER_IMAGE_VERSION && + echo "Successfully built docker image ${FULL_IMAGE_NAME}" + +if [ "${PUBLISH}" = true ]; then + echo "Pushing docker image ${FULL_IMAGE_NAME} to ECR repository to AWS account ${AWS_DEPLOYMENT_ACCOUNT}" + if [ "${PUBLISH}" = true ]; then + echo "logging in" + aws ecr get-login-password --region "${AWS_REGION}" | docker login --username AWS --password-stdin "${AWS_REPOSITORY}" + fi + docker push "${FULL_IMAGE_NAME}:latest" && docker push "${FULL_IMAGE_NAME}:${VERSION}" && + echo "Successfully pushed docker image ${FULL_IMAGE_NAME} to ECR repository" +fi diff --git a/bin/generate-release-notes b/bin/generate-release-notes new file mode 100755 index 0000000..1bf22d7 --- /dev/null +++ b/bin/generate-release-notes @@ -0,0 +1,16 @@ +#!/bin/bash +# Wrapper script to run release notes generator with proper environment + +# Get the directory where this script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )" + +# Set PYTHONPATH to include the project root +export PYTHONPATH="$PROJECT_ROOT:$PYTHONPATH" + +# Run the generator with python from venv if available, otherwise use system python +if [ -f "$PROJECT_ROOT/.venv/bin/python" ]; then + exec "$PROJECT_ROOT/.venv/bin/python" "$PROJECT_ROOT/tools/generate_release_notes.py" "$@" +else + exec python "$PROJECT_ROOT/tools/generate_release_notes.py" "$@" +fi diff --git a/cognee-frontend/.dockerignore b/cognee-frontend/.dockerignore new file mode 100644 index 0000000..f74c781 --- /dev/null +++ b/cognee-frontend/.dockerignore @@ -0,0 +1,2 @@ +.next +node_modules diff --git a/cognee-frontend/.env.template b/cognee-frontend/.env.template new file mode 100644 index 0000000..53d6814 --- /dev/null +++ b/cognee-frontend/.env.template @@ -0,0 +1,3 @@ +NEXT_PUBLIC_BACKEND_API_URL=http://localhost:8000/api +# The shared frontend defaults to cloud mode; OSS/local runs must opt out. +NEXT_PUBLIC_IS_CLOUD_ENVIRONMENT=false diff --git a/cognee-frontend/.eslintrc.json b/cognee-frontend/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/cognee-frontend/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/cognee-frontend/.gitignore b/cognee-frontend/.gitignore new file mode 100644 index 0000000..fd3dbb5 --- /dev/null +++ b/cognee-frontend/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/cognee-frontend/.prettierignore b/cognee-frontend/.prettierignore new file mode 100644 index 0000000..0e75fe5 --- /dev/null +++ b/cognee-frontend/.prettierignore @@ -0,0 +1,3 @@ +node_modules +dist +coverage diff --git a/cognee-frontend/.prettierrc b/cognee-frontend/.prettierrc new file mode 100644 index 0000000..b5c6918 --- /dev/null +++ b/cognee-frontend/.prettierrc @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": false, + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/cognee-frontend/Dockerfile b/cognee-frontend/Dockerfile new file mode 100644 index 0000000..2d47482 --- /dev/null +++ b/cognee-frontend/Dockerfile @@ -0,0 +1,22 @@ +# Use an official Node.js runtime as a parent image +FROM node:22-alpine + +# Set the working directory to /app +WORKDIR /app + +# Copy package.json and package-lock.json to the working directory +COPY package.json package-lock.json ./ + +# Install any needed packages specified in package.json +RUN npm ci +RUN npm rebuild lightningcss + +# Copy the rest of the application code to the working directory +COPY src ./src +COPY public ./public +COPY next.config.mjs . +COPY postcss.config.mjs . +COPY tsconfig.json . + +# Build the app and run it +CMD ["npm", "run", "dev"] diff --git a/cognee-frontend/README.md b/cognee-frontend/README.md new file mode 100644 index 0000000..c403366 --- /dev/null +++ b/cognee-frontend/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/cognee-frontend/eslint.config.mjs b/cognee-frontend/eslint.config.mjs new file mode 100644 index 0000000..c85fb67 --- /dev/null +++ b/cognee-frontend/eslint.config.mjs @@ -0,0 +1,16 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript"), +]; + +export default eslintConfig; diff --git a/cognee-frontend/next.config.mjs b/cognee-frontend/next.config.mjs new file mode 100644 index 0000000..4678774 --- /dev/null +++ b/cognee-frontend/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/cognee-frontend/next.config.ts b/cognee-frontend/next.config.ts new file mode 100644 index 0000000..e876d15 --- /dev/null +++ b/cognee-frontend/next.config.ts @@ -0,0 +1,12 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + images: { + remotePatterns: [{ + protocol: "https", + hostname: "lh3.googleusercontent.com", + }], + }, +}; + +export default nextConfig; diff --git a/cognee-frontend/package-lock.json b/cognee-frontend/package-lock.json new file mode 100644 index 0000000..4f07aca --- /dev/null +++ b/cognee-frontend/package-lock.json @@ -0,0 +1,13114 @@ +{ + "name": "cognee-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cognee-frontend", + "version": "1.0.0", + "dependencies": { + "@mantine/core": "^8.3.13", + "@mantine/dropzone": "^8.3.14", + "@mantine/form": "^8.3.13", + "@mantine/hooks": "^8.3.13", + "@mantine/modals": "^8.3.13", + "@mantine/notifications": "^8.3.14", + "classnames": "^2.5.1", + "culori": "^4.0.2", + "d3-force-3d": "^3.0.6", + "embla-carousel-autoplay": "^8.6.0", + "embla-carousel-fade": "^8.6.0", + "embla-carousel-react": "^8.6.0", + "jose": "^6.0.11", + "mantine-form-valibot-resolver": "^3.0.1", + "next": "^16.0.8", + "react": "^19.1.2", + "react-dom": "^19.1.2", + "react-error-boundary": "^6.0.0", + "react-force-graph-2d": "^1.29.0", + "react-markdown": "^10.1.0", + "uuid": "^11.1.0", + "valibot": "^1.2.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4.1.18", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/culori": "^4.0.1", + "@types/jest": "^30.0.0", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "autoprefixer": "^10.4.23", + "eslint": "^9", + "eslint-config-next": "15.3.5", + "jest": "^30.2.0", + "jest-environment-jsdom": "^30.2.0", + "jest-fetch-mock": "^3.0.3", + "postcss": "^8.5.6", + "postcss-import": "^16.1.1", + "postcss-preset-mantine": "^1.18.0", + "postcss-simple-vars": "^7.0.1", + "tailwindcss": "^4.0.0", + "ts-jest": "^29.4.6", + "ts-node": "^10.9.2", + "typescript": "^5" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.31.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.19", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz", + "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment-jsdom-abstract": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.4.1.tgz", + "integrity": "sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/jsdom": "^21.1.7", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/@jest/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.4.1", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", + "@types/node": "*", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mantine/core": { + "version": "8.3.18", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.18.tgz", + "integrity": "sha512-9tph1lTVogKPjTx02eUxDUOdXacPzK62UuSqb4TdGliI54/Xgxftq0Dfqu6XuhCxn9J5MDJaNiLDvL/1KRkYqA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.27.16", + "clsx": "^2.1.1", + "react-number-format": "^5.4.4", + "react-remove-scroll": "^2.7.1", + "react-textarea-autosize": "8.5.9", + "type-fest": "^4.41.0" + }, + "peerDependencies": { + "@mantine/hooks": "8.3.18", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/dropzone": { + "version": "8.3.18", + "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-8.3.18.tgz", + "integrity": "sha512-GaYUUl/382R7hl1g6heTCZ5a6T5x6qYPg0oID6ik/J0j7e5+XMZyTH5ITpaqpsBQ09GKKsF5y3iNehpSby8Kew==", + "license": "MIT", + "dependencies": { + "react-dropzone": "15.0.0" + }, + "peerDependencies": { + "@mantine/core": "8.3.18", + "@mantine/hooks": "8.3.18", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/form": { + "version": "8.3.18", + "resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.3.18.tgz", + "integrity": "sha512-r5OGLJWTkmIruFjRZRZy9oA7maNYlyt50jB4Pmd2X5360WOmJLd4KH8MFhHZQC7vN+z8/rmBl3t3XGAR2I8xig==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "klona": "^2.0.6" + }, + "peerDependencies": { + "react": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/hooks": { + "version": "8.3.18", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.18.tgz", + "integrity": "sha512-QoWr9+S8gg5050TQ06aTSxtlpGjYOpIllRbjYYXlRvZeTsUqiTbVfvQROLexu4rEaK+yy9Wwriwl9PMRgbLqPw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/modals": { + "version": "8.3.18", + "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-8.3.18.tgz", + "integrity": "sha512-JfPDS4549L314SxFPC1x6CbKwzh82OdnIzwgMxPCVNsWLKV2vEHHUH/fzUYj4Wli6IBrsW4cufjMj9BTj3hm3Q==", + "license": "MIT", + "peerDependencies": { + "@mantine/core": "8.3.18", + "@mantine/hooks": "8.3.18", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/notifications": { + "version": "8.3.18", + "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.3.18.tgz", + "integrity": "sha512-IpQ0lmwbigTBbZCR6iSYWqIOKEx1tlcd7PcEJ5M5X1qeVSY/N3mmDQt1eJmObvcyDeL5cTJMbSA9UPqhRqo9jw==", + "license": "MIT", + "dependencies": { + "@mantine/store": "8.3.18", + "react-transition-group": "4.4.5" + }, + "peerDependencies": { + "@mantine/core": "8.3.18", + "@mantine/hooks": "8.3.18", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/store": { + "version": "8.3.18", + "resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.3.18.tgz", + "integrity": "sha512-i+QRTLmZzLldea0egtUVnGALd6UMIu8jd44nrNWBSNIXJU/8B6rMlC6gyX+l4szopZSuOaaNJIXkqRdC1gQsVg==", + "license": "MIT", + "peerDependencies": { + "react": "^18.x || ^19.x" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", + "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.5.tgz", + "integrity": "sha512-BZwWPGfp9po/rAnJcwUBaM+yT/+yTWIkWdyDwc74G9jcfTrNrmsHe+hXHljV066YNdVs8cxROxX5IgMQGX190w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz", + "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz", + "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz", + "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz", + "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz", + "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz", + "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz", + "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz", + "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", + "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.10.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.10.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz", + "integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "postcss": "^8.5.10", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tweenjs/tween.js": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", + "integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/culori": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/culori/-/culori-4.0.1.tgz", + "integrity": "sha512-43M51r/22CjhbOXyGT361GZ9vncSVQ39u62x5eJdBQFviI8zWp2X5jzqg7k4M6PVgDQAClpy2bUe2dtwEgEDVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.9", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz", + "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz", + "integrity": "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/type-utils": "8.60.0", + "@typescript-eslint/utils": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.60.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.0.tgz", + "integrity": "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz", + "integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.60.0", + "@typescript-eslint/types": "^8.60.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz", + "integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz", + "integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz", + "integrity": "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/utils": "8.60.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz", + "integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz", + "integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.60.0", + "@typescript-eslint/tsconfig-utils": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz", + "integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz", + "integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/accessor-fn": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz", + "integrity": "sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.3", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-jest": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.4.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.4.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.4.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bezier-js": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-6.1.4.tgz", + "integrity": "sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvas-color-tracker": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/canvas-color-tracker/-/canvas-color-tracker-1.3.2.tgz", + "integrity": "sha512-ryQkDX26yJ3CXzb3hxUVNlg1NKE4REc5crLBq661Nxzr8TNd236SaEf2ffYLXyI5tSABSeguHLqcVq4vf9L3Zg==", + "license": "MIT", + "dependencies": { + "tinycolor2": "^1.6.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/classnames": { + "version": "2.5.1", + "license": "MIT" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/culori": { + "version": "4.0.2", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-binarytree": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force-3d": { + "version": "3.0.6", + "license": "MIT", + "dependencies": { + "d3-binarytree": "1", + "d3-dispatch": "1 - 3", + "d3-octree": "1", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-octree": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.364", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", + "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "license": "MIT" + }, + "node_modules/embla-carousel-autoplay": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.6.0.tgz", + "integrity": "sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/embla-carousel-fade": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-fade/-/embla-carousel-fade-8.6.0.tgz", + "integrity": "sha512-qaYsx5mwCz72ZrjlsXgs1nKejSrW+UhkbOMwLgfRT7w2LtdEB03nPRI06GHuHv5ac2USvbEiX2/nAHctcDwvpg==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/embla-carousel-react": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", + "license": "MIT", + "dependencies": { + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz", + "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.31.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "15.3.5", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.5.tgz", + "integrity": "sha512-oQdvnIgP68wh2RlR3MdQpvaJ94R6qEFl+lnu8ZKxPj5fsAHrSF/HlAOZcsimLw3DT6bnEQIUdbZC2Ab6sWyptg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "15.3.5", + "@rushstack/eslint-patch": "^1.10.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "license": "MIT", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "dev": true, + "license": "ISC" + }, + "node_modules/float-tooltip": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/float-tooltip/-/float-tooltip-1.7.5.tgz", + "integrity": "sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg==", + "license": "MIT", + "dependencies": { + "d3-selection": "2 - 3", + "kapsule": "^1.16", + "preact": "10" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/force-graph": { + "version": "1.51.4", + "resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.51.4.tgz", + "integrity": "sha512-TdJ2KbkoiDQ7NIRx8IPGD0mAXXpLhamS7c+b7W98b0MHG7lphnda1VOQX/98UDTsttIAdH4TcP0l0MauSnLK8w==", + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "18 - 25", + "accessor-fn": "1", + "bezier-js": "3 - 6", + "canvas-color-tracker": "^1.3", + "d3-array": "1 - 3", + "d3-drag": "2 - 3", + "d3-force-3d": "2 - 3", + "d3-scale": "1 - 4", + "d3-scale-chromatic": "1 - 3", + "d3-selection": "2 - 3", + "d3-zoom": "2 - 3", + "float-tooltip": "^1.7", + "index-array-by": "1", + "kapsule": "^1.16", + "lodash-es": "4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/index-array-by": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/index-array-by/-/index-array-by-1.4.2.tgz", + "integrity": "sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jerrypick": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/jest": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", + "import-local": "^3.2.0", + "jest-cli": "30.4.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0", + "pretty-format": "30.4.1", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "parse-json": "^5.2.0", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "jest-util": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.4.1.tgz", + "integrity": "sha512-o3nfaN4zej7qgk2X0j8Jhq/S9nAVKs2xK3QeQxeHVvpkEPxaA1yxDGydR+iVI7zPy7Cp62Aq2h3Ja46QvfWHGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/environment-jsdom-abstract": "30.4.1", + "jsdom": "^26.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" + } + }, + "node_modules/jest-haste-map": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-watcher": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.4.1", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/kapsule": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/kapsule/-/kapsule-1.16.3.tgz", + "integrity": "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg==", + "license": "MIT", + "dependencies": { + "lodash-es": "4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/mantine-form-valibot-resolver": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mantine-form-valibot-resolver/-/mantine-form-valibot-resolver-3.0.1.tgz", + "integrity": "sha512-CXm6yi5znCYI52/gXTbGOTCLlsS4Tu343W09FVvY/oMWRLLBoe+dDHznjECTIr9RNRc38Z/qMvQpjIcfHRcIhA==", + "license": "MIT", + "engines": { + "node": ">=16.6.0" + }, + "peerDependencies": { + "@mantine/form": ">=7.0.0", + "valibot": ">=1.0.0 || ^1.0.0-beta || ^1.0.0-rc" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz", + "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", + "license": "MIT", + "dependencies": { + "@next/env": "16.1.6", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.1.6", + "@next/swc-darwin-x64": "16.1.6", + "@next/swc-linux-arm64-gnu": "16.1.6", + "@next/swc-linux-arm64-musl": "16.1.6", + "@next/swc-linux-x64-gnu": "16.1.6", + "@next/swc-linux-x64-musl": "16.1.6", + "@next/swc-win32-arm64-msvc": "16.1.6", + "@next/swc-win32-x64-msvc": "16.1.6", + "sharp": "^0.34.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.1.tgz", + "integrity": "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-mixins": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-12.1.2.tgz", + "integrity": "sha512-90pSxmZVfbX9e5xCv7tI5RV1mnjdf16y89CJKbf/hD7GyOz1FCxcYMl8ZYA8Hc56dbApTKKmU9HfvgfWdCxlwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-js": "^4.0.1", + "postcss-simple-vars": "^7.0.1", + "sugarss": "^5.0.0", + "tinyglobby": "^0.2.14" + }, + "engines": { + "node": "^20.0 || ^22.0 || >=24.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-7.0.2.tgz", + "integrity": "sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-preset-mantine": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/postcss-preset-mantine/-/postcss-preset-mantine-1.18.0.tgz", + "integrity": "sha512-sP6/s1oC7cOtBdl4mw/IRKmKvYTuzpRrH/vT6v9enMU/EQEQ31eQnHcWtFghOXLH87AAthjL/Q75rLmin1oZoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-mixins": "^12.0.0", + "postcss-nested": "^7.0.2" + }, + "peerDependencies": { + "postcss": ">=8.0.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-simple-vars": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz", + "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.1" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.29.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz", + "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/promise-polyfill": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "node_modules/react-dropzone": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-15.0.0.tgz", + "integrity": "sha512-lGjYV/EoqEjEWPnmiSvH4v5IoIAwQM2W4Z1C0Q/Pw2xD0eVzKPS359BQTUMum+1fa0kH2nrKjuavmTPOGhpLPg==", + "license": "MIT", + "dependencies": { + "attr-accept": "^2.2.4", + "file-selector": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, + "node_modules/react-error-boundary": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.2.tgz", + "integrity": "sha512-3DpCr5HVdZ0caUjYE/kIHBEJN0mNP3ZCgf16c48uJ5TbWjorKVp+YG8W3XqlJ7vJAVNw6wNIImyPXmFydwmyng==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-force-graph-2d": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/react-force-graph-2d/-/react-force-graph-2d-1.29.1.tgz", + "integrity": "sha512-1Rl/1Z3xy2iTHKj6a0jRXGyiI86xUti81K+jBQZ+Oe46csaMikp47L5AjrzA9hY9fNGD63X8ffrqnvaORukCuQ==", + "license": "MIT", + "dependencies": { + "force-graph": "^1.51", + "prop-types": "15", + "react-kapsule": "^2.5" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "license": "MIT" + }, + "node_modules/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", + "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-kapsule": { + "version": "2.5.7", + "license": "MIT", + "dependencies": { + "jerrypick": "^1.1.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-number-format": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.5.tgz", + "integrity": "sha512-y8O2yHHj3w0aE9XO8d2BCcUOOdQTRSVq+WIuMlLVucAm5XNjJAy+BoOJiuQMldVYVOKTMyvVNfnbl2Oqp+YxGw==", + "license": "MIT", + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sugarss": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-5.0.1.tgz", + "integrity": "sha512-ctS5RYCBVvPoZAnzIaX5QSShK8ZiZxD5HUqSxlusvEMC+QZQIPCPOIJg6aceFX+K2rf4+SH89eu++h1Zmsr2nw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-jest": { + "version": "29.4.11", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", + "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.0", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "dev": true, + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", + "license": "MIT", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/valibot": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.4.1.tgz", + "integrity": "sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/cognee-frontend/package.json b/cognee-frontend/package.json new file mode 100644 index 0000000..331c7e9 --- /dev/null +++ b/cognee-frontend/package.json @@ -0,0 +1,65 @@ +{ + "name": "cognee-frontend", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "next lint", + "check:tracking-plan": "node scripts/check-tracking-plan.js" + }, + "dependencies": { + "@mantine/core": "^8.3.13", + "@mantine/dropzone": "^8.3.14", + "@mantine/form": "^8.3.13", + "@mantine/hooks": "^8.3.13", + "@mantine/modals": "^8.3.13", + "@mantine/notifications": "^8.3.14", + "classnames": "^2.5.1", + "culori": "^4.0.2", + "d3-force-3d": "^3.0.6", + "embla-carousel-autoplay": "^8.6.0", + "embla-carousel-fade": "^8.6.0", + "embla-carousel-react": "^8.6.0", + "jose": "^6.0.11", + "mantine-form-valibot-resolver": "^3.0.1", + "next": "^16.0.8", + "react": "^19.1.2", + "react-dom": "^19.1.2", + "react-error-boundary": "^6.0.0", + "react-force-graph-2d": "^1.29.0", + "react-markdown": "^10.1.0", + "uuid": "^11.1.0", + "valibot": "^1.2.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4.1.18", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/culori": "^4.0.1", + "@types/jest": "^30.0.0", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "autoprefixer": "^10.4.23", + "eslint": "^9", + "eslint-config-next": "15.3.5", + "jest": "^30.2.0", + "jest-environment-jsdom": "^30.2.0", + "jest-fetch-mock": "^3.0.3", + "postcss": "^8.5.6", + "postcss-import": "^16.1.1", + "postcss-preset-mantine": "^1.18.0", + "postcss-simple-vars": "^7.0.1", + "tailwindcss": "^4.0.0", + "ts-jest": "^29.4.6", + "ts-node": "^10.9.2", + "typescript": "^5" + }, + "browserslist": [ + "defaults", + "not IE 11" + ] +} diff --git a/cognee-frontend/postcss.config.mjs b/cognee-frontend/postcss.config.mjs new file mode 100644 index 0000000..e099bde --- /dev/null +++ b/cognee-frontend/postcss.config.mjs @@ -0,0 +1,19 @@ +const config = { + plugins: { + 'postcss-import': {}, + '@tailwindcss/postcss': {}, + 'autoprefixer': {}, + 'postcss-preset-mantine': {}, + 'postcss-simple-vars': { + variables: { + 'mantine-breakpoint-xs': '36em', + 'mantine-breakpoint-sm': '48em', + 'mantine-breakpoint-md': '62em', + 'mantine-breakpoint-lg': '75em', + 'mantine-breakpoint-xl': '88em', + }, + }, + }, +}; + +export default config; diff --git a/cognee-frontend/public/cognee-logo-black.svg b/cognee-frontend/public/cognee-logo-black.svg new file mode 100644 index 0000000..d90e160 --- /dev/null +++ b/cognee-frontend/public/cognee-logo-black.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/file.svg b/cognee-frontend/public/file.svg new file mode 100644 index 0000000..16fe3d3 --- /dev/null +++ b/cognee-frontend/public/file.svg @@ -0,0 +1 @@ + diff --git a/cognee-frontend/public/fonts/Parabole-Display.woff b/cognee-frontend/public/fonts/Parabole-Display.woff new file mode 100755 index 0000000..ebaf364 Binary files /dev/null and b/cognee-frontend/public/fonts/Parabole-Display.woff differ diff --git a/cognee-frontend/public/fonts/Parabole-Display.woff2 b/cognee-frontend/public/fonts/Parabole-Display.woff2 new file mode 100755 index 0000000..54d538e Binary files /dev/null and b/cognee-frontend/public/fonts/Parabole-Display.woff2 differ diff --git a/cognee-frontend/public/fonts/TWKLausanne-300.woff b/cognee-frontend/public/fonts/TWKLausanne-300.woff new file mode 100644 index 0000000..b19e557 Binary files /dev/null and b/cognee-frontend/public/fonts/TWKLausanne-300.woff differ diff --git a/cognee-frontend/public/fonts/TWKLausanne-300.woff2 b/cognee-frontend/public/fonts/TWKLausanne-300.woff2 new file mode 100644 index 0000000..b1ab539 Binary files /dev/null and b/cognee-frontend/public/fonts/TWKLausanne-300.woff2 differ diff --git a/cognee-frontend/public/fonts/TWKLausanne-700.woff b/cognee-frontend/public/fonts/TWKLausanne-700.woff new file mode 100644 index 0000000..189be25 Binary files /dev/null and b/cognee-frontend/public/fonts/TWKLausanne-700.woff differ diff --git a/cognee-frontend/public/fonts/TWKLausanne-700.woff2 b/cognee-frontend/public/fonts/TWKLausanne-700.woff2 new file mode 100644 index 0000000..903d03b Binary files /dev/null and b/cognee-frontend/public/fonts/TWKLausanne-700.woff2 differ diff --git a/cognee-frontend/public/globe.svg b/cognee-frontend/public/globe.svg new file mode 100644 index 0000000..c7215fe --- /dev/null +++ b/cognee-frontend/public/globe.svg @@ -0,0 +1 @@ + diff --git a/cognee-frontend/public/images/avatars/orr-kowarsky-avatar.png b/cognee-frontend/public/images/avatars/orr-kowarsky-avatar.png new file mode 100644 index 0000000..70094f1 Binary files /dev/null and b/cognee-frontend/public/images/avatars/orr-kowarsky-avatar.png differ diff --git a/cognee-frontend/public/images/backgrounds/build-smarter-agents-your-way-background.svg b/cognee-frontend/public/images/backgrounds/build-smarter-agents-your-way-background.svg new file mode 100644 index 0000000..96198f3 --- /dev/null +++ b/cognee-frontend/public/images/backgrounds/build-smarter-agents-your-way-background.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/images/icons/arrow-right.svg b/cognee-frontend/public/images/icons/arrow-right.svg new file mode 100644 index 0000000..17314b2 --- /dev/null +++ b/cognee-frontend/public/images/icons/arrow-right.svg @@ -0,0 +1,4 @@ + + + + diff --git a/cognee-frontend/public/images/icons/avatar.svg b/cognee-frontend/public/images/icons/avatar.svg new file mode 100644 index 0000000..db87b3d --- /dev/null +++ b/cognee-frontend/public/images/icons/avatar.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/images/icons/chevron-right.svg b/cognee-frontend/public/images/icons/chevron-right.svg new file mode 100644 index 0000000..5018cee --- /dev/null +++ b/cognee-frontend/public/images/icons/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/images/icons/cognee-logo.svg b/cognee-frontend/public/images/icons/cognee-logo.svg new file mode 100644 index 0000000..8f5dea2 --- /dev/null +++ b/cognee-frontend/public/images/icons/cognee-logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/images/icons/copy.svg b/cognee-frontend/public/images/icons/copy.svg new file mode 100644 index 0000000..d78ff19 --- /dev/null +++ b/cognee-frontend/public/images/icons/copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/cognee-frontend/public/images/icons/database.svg b/cognee-frontend/public/images/icons/database.svg new file mode 100644 index 0000000..bb5c88e --- /dev/null +++ b/cognee-frontend/public/images/icons/database.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/cognee-frontend/public/images/icons/disc.svg b/cognee-frontend/public/images/icons/disc.svg new file mode 100644 index 0000000..6743320 --- /dev/null +++ b/cognee-frontend/public/images/icons/disc.svg @@ -0,0 +1,4 @@ + + + + diff --git a/cognee-frontend/public/images/icons/home.svg b/cognee-frontend/public/images/icons/home.svg new file mode 100644 index 0000000..216eef8 --- /dev/null +++ b/cognee-frontend/public/images/icons/home.svg @@ -0,0 +1,4 @@ + + + + diff --git a/cognee-frontend/public/images/icons/message-square.svg b/cognee-frontend/public/images/icons/message-square.svg new file mode 100644 index 0000000..953cacb --- /dev/null +++ b/cognee-frontend/public/images/icons/message-square.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/images/icons/pullrequest.svg b/cognee-frontend/public/images/icons/pullrequest.svg new file mode 100644 index 0000000..e2c9c5b --- /dev/null +++ b/cognee-frontend/public/images/icons/pullrequest.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/cognee-frontend/public/images/icons/smiley-face-icon.svg b/cognee-frontend/public/images/icons/smiley-face-icon.svg new file mode 100644 index 0000000..6ae855e --- /dev/null +++ b/cognee-frontend/public/images/icons/smiley-face-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/images/icons/x.svg b/cognee-frontend/public/images/icons/x.svg new file mode 100644 index 0000000..0325e19 --- /dev/null +++ b/cognee-frontend/public/images/icons/x.svg @@ -0,0 +1,4 @@ + + + + diff --git a/cognee-frontend/public/images/logos/atlassian-logo.png b/cognee-frontend/public/images/logos/atlassian-logo.png new file mode 100644 index 0000000..4f34d43 Binary files /dev/null and b/cognee-frontend/public/images/logos/atlassian-logo.png differ diff --git a/cognee-frontend/public/images/logos/autodesk-logo.png b/cognee-frontend/public/images/logos/autodesk-logo.png new file mode 100644 index 0000000..ce3b493 Binary files /dev/null and b/cognee-frontend/public/images/logos/autodesk-logo.png differ diff --git a/cognee-frontend/public/images/logos/aws-logo.png b/cognee-frontend/public/images/logos/aws-logo.png new file mode 100644 index 0000000..daa8388 Binary files /dev/null and b/cognee-frontend/public/images/logos/aws-logo.png differ diff --git a/cognee-frontend/public/images/logos/bayer-logo.png b/cognee-frontend/public/images/logos/bayer-logo.png new file mode 100644 index 0000000..af5a172 Binary files /dev/null and b/cognee-frontend/public/images/logos/bayer-logo.png differ diff --git a/cognee-frontend/public/images/logos/blended-logo.png b/cognee-frontend/public/images/logos/blended-logo.png new file mode 100644 index 0000000..7e30a70 Binary files /dev/null and b/cognee-frontend/public/images/logos/blended-logo.png differ diff --git a/cognee-frontend/public/images/logos/botpress-logo.png b/cognee-frontend/public/images/logos/botpress-logo.png new file mode 100644 index 0000000..ea6014a Binary files /dev/null and b/cognee-frontend/public/images/logos/botpress-logo.png differ diff --git a/cognee-frontend/public/images/logos/infosys-logo.png b/cognee-frontend/public/images/logos/infosys-logo.png new file mode 100644 index 0000000..3e25325 Binary files /dev/null and b/cognee-frontend/public/images/logos/infosys-logo.png differ diff --git a/cognee-frontend/public/images/logos/kovai-logo.png b/cognee-frontend/public/images/logos/kovai-logo.png new file mode 100644 index 0000000..590229d Binary files /dev/null and b/cognee-frontend/public/images/logos/kovai-logo.png differ diff --git a/cognee-frontend/public/images/logos/redis-logo.png b/cognee-frontend/public/images/logos/redis-logo.png new file mode 100644 index 0000000..17d566f Binary files /dev/null and b/cognee-frontend/public/images/logos/redis-logo.png differ diff --git a/cognee-frontend/public/images/logos/rentpost-logo.png b/cognee-frontend/public/images/logos/rentpost-logo.png new file mode 100644 index 0000000..12353ea Binary files /dev/null and b/cognee-frontend/public/images/logos/rentpost-logo.png differ diff --git a/cognee-frontend/public/images/logos/reply-logo.png b/cognee-frontend/public/images/logos/reply-logo.png new file mode 100644 index 0000000..e788dd0 Binary files /dev/null and b/cognee-frontend/public/images/logos/reply-logo.png differ diff --git a/cognee-frontend/public/images/logos/ribbon-logo.png b/cognee-frontend/public/images/logos/ribbon-logo.png new file mode 100644 index 0000000..6ab5c9c Binary files /dev/null and b/cognee-frontend/public/images/logos/ribbon-logo.png differ diff --git a/cognee-frontend/public/images/logos/splunk-logo.png b/cognee-frontend/public/images/logos/splunk-logo.png new file mode 100644 index 0000000..a942b2c Binary files /dev/null and b/cognee-frontend/public/images/logos/splunk-logo.png differ diff --git a/cognee-frontend/public/images/logos/studocu-logo.png b/cognee-frontend/public/images/logos/studocu-logo.png new file mode 100644 index 0000000..807ec04 Binary files /dev/null and b/cognee-frontend/public/images/logos/studocu-logo.png differ diff --git a/cognee-frontend/public/next.svg b/cognee-frontend/public/next.svg new file mode 100644 index 0000000..5bb00d4 --- /dev/null +++ b/cognee-frontend/public/next.svg @@ -0,0 +1 @@ + diff --git a/cognee-frontend/public/vercel.svg b/cognee-frontend/public/vercel.svg new file mode 100644 index 0000000..5215157 --- /dev/null +++ b/cognee-frontend/public/vercel.svg @@ -0,0 +1 @@ + diff --git a/cognee-frontend/public/videos/cognee-graph.mov b/cognee-frontend/public/videos/cognee-graph.mov new file mode 100644 index 0000000..fbfddb4 Binary files /dev/null and b/cognee-frontend/public/videos/cognee-graph.mov differ diff --git a/cognee-frontend/public/videos/cognee-mascot-wave.mp4 b/cognee-frontend/public/videos/cognee-mascot-wave.mp4 new file mode 100644 index 0000000..84e0e75 Binary files /dev/null and b/cognee-frontend/public/videos/cognee-mascot-wave.mp4 differ diff --git a/cognee-frontend/public/videos/full-demo-poster.jpg b/cognee-frontend/public/videos/full-demo-poster.jpg new file mode 100644 index 0000000..b1cbf20 Binary files /dev/null and b/cognee-frontend/public/videos/full-demo-poster.jpg differ diff --git a/cognee-frontend/public/videos/full-demo.mp4 b/cognee-frontend/public/videos/full-demo.mp4 new file mode 100644 index 0000000..eb4622a Binary files /dev/null and b/cognee-frontend/public/videos/full-demo.mp4 differ diff --git a/cognee-frontend/public/videos/mascot-excited.mp4 b/cognee-frontend/public/videos/mascot-excited.mp4 new file mode 100644 index 0000000..bb9903a Binary files /dev/null and b/cognee-frontend/public/videos/mascot-excited.mp4 differ diff --git a/cognee-frontend/public/videos/mascot-purple-glow.mp4 b/cognee-frontend/public/videos/mascot-purple-glow.mp4 new file mode 100644 index 0000000..55377c6 Binary files /dev/null and b/cognee-frontend/public/videos/mascot-purple-glow.mp4 differ diff --git a/cognee-frontend/public/videos/mascot-waiting.mp4 b/cognee-frontend/public/videos/mascot-waiting.mp4 new file mode 100644 index 0000000..bb9903a Binary files /dev/null and b/cognee-frontend/public/videos/mascot-waiting.mp4 differ diff --git a/cognee-frontend/public/videos/mascot-wink.mp4 b/cognee-frontend/public/videos/mascot-wink.mp4 new file mode 100644 index 0000000..3052722 Binary files /dev/null and b/cognee-frontend/public/videos/mascot-wink.mp4 differ diff --git a/cognee-frontend/public/videos/rotating-logo-background.mp4 b/cognee-frontend/public/videos/rotating-logo-background.mp4 new file mode 100644 index 0000000..93630c8 Binary files /dev/null and b/cognee-frontend/public/videos/rotating-logo-background.mp4 differ diff --git a/cognee-frontend/public/visuals/link-to.svg b/cognee-frontend/public/visuals/link-to.svg new file mode 100644 index 0000000..f576fbd --- /dev/null +++ b/cognee-frontend/public/visuals/link-to.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/visuals/logos/claude.svg b/cognee-frontend/public/visuals/logos/claude.svg new file mode 100644 index 0000000..0097636 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/claude.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/visuals/logos/cline.svg b/cognee-frontend/public/visuals/logos/cline.svg new file mode 100644 index 0000000..ef6d710 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/cline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/cognee-frontend/public/visuals/logos/codex.svg b/cognee-frontend/public/visuals/logos/codex.svg new file mode 100644 index 0000000..f33f5e5 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/codex.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/cognee.svg b/cognee-frontend/public/visuals/logos/cognee.svg new file mode 100644 index 0000000..d0307e2 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/cognee.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/cursor.svg b/cognee-frontend/public/visuals/logos/cursor.svg new file mode 100644 index 0000000..7e98934 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/cursor.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/dify.svg b/cognee-frontend/public/visuals/logos/dify.svg new file mode 100644 index 0000000..ed6236e --- /dev/null +++ b/cognee-frontend/public/visuals/logos/dify.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/gemini.svg b/cognee-frontend/public/visuals/logos/gemini.svg new file mode 100644 index 0000000..9d3e1f6 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/gemini.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/hermes.svg b/cognee-frontend/public/visuals/logos/hermes.svg new file mode 100644 index 0000000..0a3f577 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/hermes.svg @@ -0,0 +1,116 @@ + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/mcp.svg b/cognee-frontend/public/visuals/logos/mcp.svg new file mode 100644 index 0000000..daee957 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/mcp.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/visuals/logos/n8n.svg b/cognee-frontend/public/visuals/logos/n8n.svg new file mode 100644 index 0000000..bba7dd1 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/n8n.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/openclaw.svg b/cognee-frontend/public/visuals/logos/openclaw.svg new file mode 100644 index 0000000..704903f --- /dev/null +++ b/cognee-frontend/public/visuals/logos/openclaw.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/skills.svg b/cognee-frontend/public/visuals/logos/skills.svg new file mode 100644 index 0000000..bc0a1d8 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/skills.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/visuals/logos/vscode.svg b/cognee-frontend/public/visuals/logos/vscode.svg new file mode 100644 index 0000000..bca013b --- /dev/null +++ b/cognee-frontend/public/visuals/logos/vscode.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/logos/windsurf.svg b/cognee-frontend/public/visuals/logos/windsurf.svg new file mode 100644 index 0000000..ff4be38 --- /dev/null +++ b/cognee-frontend/public/visuals/logos/windsurf.svg @@ -0,0 +1,3 @@ + + + diff --git a/cognee-frontend/public/visuals/quickstart-1.svg b/cognee-frontend/public/visuals/quickstart-1.svg new file mode 100644 index 0000000..7e71745 --- /dev/null +++ b/cognee-frontend/public/visuals/quickstart-1.svg @@ -0,0 +1,53 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
diff --git a/cognee-frontend/public/visuals/quickstart-2.svg b/cognee-frontend/public/visuals/quickstart-2.svg new file mode 100644 index 0000000..b1c09f5 --- /dev/null +++ b/cognee-frontend/public/visuals/quickstart-2.svg @@ -0,0 +1,50 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
diff --git a/cognee-frontend/public/visuals/quickstart-3.svg b/cognee-frontend/public/visuals/quickstart-3.svg new file mode 100644 index 0000000..f8960d5 --- /dev/null +++ b/cognee-frontend/public/visuals/quickstart-3.svg @@ -0,0 +1,261 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
diff --git a/cognee-frontend/public/visuals/usecase-docs.svg b/cognee-frontend/public/visuals/usecase-docs.svg new file mode 100644 index 0000000..57ab3a3 --- /dev/null +++ b/cognee-frontend/public/visuals/usecase-docs.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/usecase-investment.svg b/cognee-frontend/public/visuals/usecase-investment.svg new file mode 100644 index 0000000..bd81df3 --- /dev/null +++ b/cognee-frontend/public/visuals/usecase-investment.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/usecase-memory-agents.svg b/cognee-frontend/public/visuals/usecase-memory-agents.svg new file mode 100644 index 0000000..019a3f8 --- /dev/null +++ b/cognee-frontend/public/visuals/usecase-memory-agents.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/usecase-sales.svg b/cognee-frontend/public/visuals/usecase-sales.svg new file mode 100644 index 0000000..6126691 --- /dev/null +++ b/cognee-frontend/public/visuals/usecase-sales.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/visuals/usecase-second-brain.svg b/cognee-frontend/public/visuals/usecase-second-brain.svg new file mode 100644 index 0000000..1d50fc3 --- /dev/null +++ b/cognee-frontend/public/visuals/usecase-second-brain.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/cognee-frontend/public/window.svg b/cognee-frontend/public/window.svg new file mode 100644 index 0000000..d05e7a1 --- /dev/null +++ b/cognee-frontend/public/window.svg @@ -0,0 +1 @@ + diff --git a/cognee-frontend/src/app/(app)/DashboardEntry.tsx b/cognee-frontend/src/app/(app)/DashboardEntry.tsx new file mode 100644 index 0000000..30af96e --- /dev/null +++ b/cognee-frontend/src/app/(app)/DashboardEntry.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import OverviewPage from "./dashboard/OverviewPage"; + +export default function DashboardEntry() { + const router = useRouter(); + // Synchronous, no network round-trip: local mode is exactly one browser + // talking to exactly one backend, so these flags reliably answer "has this + // user ever finished (or skipped) onboarding". Brand new / freshly-wiped + // installs go straight to /onboarding instead of mounting the dashboard + // first, which would otherwise flash on screen while OverviewPage's own + // async check (datasets/runs + /api/user-app-state) resolves. + const [shouldShowDashboard] = useState(() => { + if (typeof window === "undefined") return true; + try { + return ( + sessionStorage.getItem("cognee-onboarding-skipped") !== null || + localStorage.getItem("cognee-onboarding-complete") !== null + ); + } catch { + return true; + } + }); + + useEffect(() => { + if (!shouldShowDashboard) { + router.replace("/onboarding"); + } + }, [shouldShowDashboard, router]); + + // Renders nothing while redirecting — OverviewPage still re-validates this + // decision against real backend data once it mounts (existing datasets/ + // runs + /api/user-app-state check), so a stale flag can't strand a + // returning user with real data on /onboarding. + if (!shouldShowDashboard) return null; + return ; +} diff --git a/cognee-frontend/src/app/(app)/DashboardEntryLoader.tsx b/cognee-frontend/src/app/(app)/DashboardEntryLoader.tsx new file mode 100644 index 0000000..8026181 --- /dev/null +++ b/cognee-frontend/src/app/(app)/DashboardEntryLoader.tsx @@ -0,0 +1,16 @@ +"use client"; + +import dynamicImport from "next/dynamic"; + +// ssr: false is load-bearing: DashboardEntry's redirect decision reads +// localStorage, which doesn't exist during server rendering. Without this, +// the server-rendered HTML falls back to "show the dashboard" (no window), +// so the browser paints a full dashboard for a moment before client JS +// hydrates, runs the real check, and swaps to /onboarding. next/dynamic's +// ssr:false option is only usable from a Client Component, hence this +// wrapper around the Server Component page.tsx. +const DashboardEntry = dynamicImport(() => import("./DashboardEntry"), { ssr: false }); + +export default function DashboardEntryLoader() { + return ; +} diff --git a/cognee-frontend/src/app/(app)/api-keys/ApiKeysPage.tsx b/cognee-frontend/src/app/(app)/api-keys/ApiKeysPage.tsx new file mode 100644 index 0000000..810c521 --- /dev/null +++ b/cognee-frontend/src/app/(app)/api-keys/ApiKeysPage.tsx @@ -0,0 +1,412 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { useCogniInstance, useTenant } from "@/modules/tenant/TenantProvider"; +import UpgradeBanner from "@/ui/elements/UpgradeBanner"; +import PageLoading from "@/ui/elements/PageLoading"; +import SkeletonBar from "@/ui/elements/SkeletonBar"; +import getApiKeys from "@/modules/apiKeys/getApiKeys"; +import createApiKey from "@/modules/apiKeys/createAPIKey"; +import deleteApiKey from "@/modules/apiKeys/deleteAPIKey"; +import getMyUserId from "@/modules/apiKeys/getMyUserId"; +import { TrackPageView, trackEvent } from "@/modules/analytics"; +import { isCloudEnvironment } from "@/utils"; + +function WarmingUpBadge() { + return ( + + warming up… + + ); +} + +interface ApiKey { + id: string; + name: string; + label: string; + key: string; + isNew?: boolean; +} + +function CopyIcon() { + return ( + + + + + ); +} + +function CheckIcon() { + return ( + + + + ); +} + +const localApiUrl = process.env.NEXT_PUBLIC_LOCAL_API_URL || "http://localhost:8000"; + +export default function ApiKeysPage() { + const { cogniInstance, serviceUrl, isInitializing } = useCogniInstance(); + const { tenant, hasAccess, tenantReady } = useTenant(); + const isCloud = isCloudEnvironment(); + // In cloud, never fall back to localhost — show the real tenant URL when known, + // otherwise treat as provisioning. Only local/OSS mode uses localApiUrl. + const baseUrl = isCloud ? serviceUrl : (serviceUrl || localApiUrl); + const urlProvisioning = isCloud && !serviceUrl; + const [keys, setKeys] = useState([]); + const [loading, setLoading] = useState(true); + const [creating, setCreating] = useState(false); + const [copiedId, setCopiedId] = useState(null); + const [copiedField, setCopiedField] = useState(null); + const [showCreateModal, setShowCreateModal] = useState(false); + const [newName, setNewName] = useState(""); + const [userId, setUserId] = useState(null); + + // Use tenant ID from context (set during provisioning), not from /users/me + const tenantId = tenant?.tenant_id || null; + const isDev = serviceUrl?.includes("dev-aws") || serviceUrl?.includes("dev.cloud"); + const apiDocsUrl = serviceUrl ? `${serviceUrl}/docs` : null; + + useEffect(() => { + if (isInitializing || !cogniInstance) return; + loadKeys(); + getMyUserId(cogniInstance).then((id) => { if (id) setUserId(id); }).catch(() => {}); + }, [cogniInstance, isInitializing]); + + async function loadKeys() { + if (!cogniInstance) return; + try { + const data = await getApiKeys(cogniInstance); + setKeys(Array.isArray(data) ? data.map((k) => ({ ...k, name: k.name || k.label || "", isNew: false })) : []); + } catch { + setKeys([]); + } finally { + setLoading(false); + } + } + + async function handleCreate() { + if (!newName.trim() || !cogniInstance) return; + setCreating(true); + try { + const created = await createApiKey(cogniInstance, { name: newName.trim() }); + trackEvent({ pageName: "API Keys", eventName: "api_key_created", additionalProperties: { key_name: newName.trim() } }); + setNewName(""); + setShowCreateModal(false); + // Reload the list, then surface the full key once — this is the only + // time the server returns it in clear text. + await loadKeys(); + setKeys((prev) => prev.map((k) => (k.id === created.id ? { ...k, key: created.key, isNew: true } : k))); + } catch (err) { + console.error("Failed to create key:", err); + } finally { + setCreating(false); + } + } + + async function handleRevoke(id: string) { + if (!cogniInstance) return; + try { + await deleteApiKey(cogniInstance, id); + trackEvent({ pageName: "API Keys", eventName: "api_key_revoked", additionalProperties: { key_id: id } }); + setKeys((prev) => prev.filter((k) => k.id !== id)); + } catch (err) { + console.error("Failed to revoke key:", err); + } + } + + function handleCopy(id: string, key: string) { + navigator.clipboard.writeText(key).catch(() => {}); + trackEvent({ pageName: "API Keys", eventName: "api_key_copied" }); + setCopiedId(id); + setTimeout(() => setCopiedId(null), 1500); + } + + if (isInitializing) { + return ( + <> + ); + } + + return ( +
+ {!hasAccess && } + {/* Header */} +
+
+

API Keys

+ Manage keys for programmatic access to the Cognee API. +
+ +
+ + {/* Create modal */} + {showCreateModal && ( +
setShowCreateModal(false)}> +
e.stopPropagation()} style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 12, padding: 24, width: 420, display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 16px 48px rgba(0,0,0,0.4)" }}> +

Create API key

+

Give your key a name to identify it later.

+ setNewName(e.target.value)} + onKeyDown={(e) => { if (e.key === "Enter") handleCreate(); }} + placeholder="e.g. Production, CI/CD, Local Dev..." + style={{ width: "100%", height: 40, background: "rgba(255,255,255,0.06)", border: "1px solid rgba(255,255,255,0.12)", borderRadius: 8, paddingInline: 14, fontSize: 14, color: "#EDECEA", fontFamily: "inherit", outline: "none", boxSizing: "border-box" }} + onFocus={(e) => { e.target.style.borderColor = "#6510F4"; e.target.style.boxShadow = "0 0 0 3px rgba(188,155,255,0.10)"; }} + onBlur={(e) => { e.target.style.borderColor = "rgba(255,255,255,0.12)"; e.target.style.boxShadow = "none"; }} + /> +
+ + +
+
+
+ )} + + {/* Connection details */} +
+
+ + Connection Details + Use these with Claude, MCP, or any API client +
+ +
+ {/* API Base URL */} +
+ API Base URL +
+ {urlProvisioning ? ( + + ) : ( + {baseUrl} + )} + {isCloud && !tenantReady && } + {!urlProvisioning && baseUrl && } +
+
+ + {/* Tenant ID */} +
+ Tenant ID +
+ {tenantId ? ( + <> + {tenantId} + + + ) : isCloud ? ( + + ) : ( + Not assigned (local mode) + )} +
+
+ + {/* User ID */} +
+ User ID +
+ {userId ? ( + <> + {userId} + + + ) : ( + + )} +
+
+
+ + {/* Auth header hint */} +
+ X-Api-Key: {""}{tenantId ? ` • X-Tenant-Id: ${tenantId}` : ""} + ${tenantId ? `\nX-Tenant-Id: ${tenantId}` : ""}`} copiedField={copiedField} setCopiedField={setCopiedField} light /> +
+
+ + {/* Documentation links */} +
+ {isCloud && ( + trackEvent({ pageName: "API Keys", eventName: "click_out", additionalProperties: { target_url: isDev ? "https://api.dev-aws.cognee.ai/docs" : "https://api.aws.cognee.ai/docs" } })} + style={{ flex: 1, display: "flex", alignItems: "center", gap: 10, background: "rgba(255,255,255,0.06)", backdropFilter: "blur(12px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 10, padding: "14px 18px", textDecoration: "none", transition: "border-color 150ms" }} + className="hover:border-[#6510F4]" + > + +
+ API Reference + Interactive Swagger docs for the shared API +
+ +
+ )} + {apiDocsUrl && ( + trackEvent({ pageName: "API Keys", eventName: "click_out", additionalProperties: { target_url: apiDocsUrl! } })} + style={{ flex: 1, display: "flex", alignItems: "center", gap: 10, background: "rgba(255,255,255,0.06)", backdropFilter: "blur(12px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 10, padding: "14px 18px", textDecoration: "none", transition: "border-color 150ms" }} + className="hover:border-[#6510F4]" + > + +
+ {isCloud ? "API Tenant Reference" : "API Reference"} + {isCloud ? "Swagger docs for your tenant instance" : "Interactive Swagger docs for your local backend"} +
+ +
+ )} +
+ + {/* Info banner */} +
+ + + + + API keys grant full access to your account. Keep them secret — do not share keys in client-side code or public repositories. Use environment variables instead. + +
+ + {/* Table */} +
+ {/* Header */} +
+ Name + Key + +
+ + {/* Rows */} + {loading && ( + <> + {Array.from({ length: 4 }).map((_, i) => ( +
+
+ +
+
+ +
+
+ +
+
+ ))} + + )} + {!loading && keys.map((k) => ( +
+
+ {k.name || "Unnamed"} +
+
+ {k.isNew ? ( + /* Show full key once for newly created keys */ +
+ {k.key} + +
+ ) : ( +
+ {k.label || k.key} + +
+ )} +
+
+ +
+
+ ))} + + {!loading && keys.length === 0 && ( +
+ + + + No API keys yet + Create one to connect agents or use the API programmatically. + +
+ )} +
+
+ ); +} + +const COPY_FIELD_MAP: Record = { url: "api_base_url", tenant: "tenant_id", user: "user_id", header: "auth_header" }; + +function CopyBtn({ id, text, copiedField, setCopiedField, light }: { id: string; text: string; copiedField: string | null; setCopiedField: (v: string | null) => void; light?: boolean }) { + const isCopied = copiedField === id; + return ( + + ); +} diff --git a/cognee-frontend/src/app/(app)/api-keys/loading.tsx b/cognee-frontend/src/app/(app)/api-keys/loading.tsx new file mode 100644 index 0000000..22d2f06 --- /dev/null +++ b/cognee-frontend/src/app/(app)/api-keys/loading.tsx @@ -0,0 +1,2 @@ +import PageLoading from "@/ui/elements/PageLoading"; +export default function Loading() { return ; } diff --git a/cognee-frontend/src/app/(app)/api-keys/page.tsx b/cognee-frontend/src/app/(app)/api-keys/page.tsx new file mode 100644 index 0000000..0c25113 --- /dev/null +++ b/cognee-frontend/src/app/(app)/api-keys/page.tsx @@ -0,0 +1,7 @@ +export const dynamic = "force-dynamic"; + +import ApiKeysPage from "./ApiKeysPage"; + +export default function Page() { + return ; +} diff --git a/cognee-frontend/src/app/(app)/connections/page.tsx b/cognee-frontend/src/app/(app)/connections/page.tsx new file mode 100644 index 0000000..0bf046e --- /dev/null +++ b/cognee-frontend/src/app/(app)/connections/page.tsx @@ -0,0 +1,792 @@ +"use client"; + +import { useEffect, useState } from "react"; +import Link from "next/link"; +import { useCogniInstance } from "@/modules/tenant/TenantProvider"; +import { useFilter } from "@/ui/layout/FilterContext"; +import { listSessions, type SessionRow } from "@/modules/sessions/getSessions"; +import { trackEvent, TrackPageView } from "@/modules/analytics"; + +interface Agent { + id: string; + email: string; + agent_type: string; + agent_short_id: string; + is_agent: boolean; + is_default: boolean; + status: string; + api_key_count: number; + created_at: string | null; + last_active: string | null; +} + +interface CreatedAgent { + agentId: string; + agentEmail: string; + agentApiKey: string; +} + +interface Dataset { id: string; name: string; ownerId?: string } + +function timeAgo(dateStr: string): string { + const diff = Date.now() - new Date(dateStr).getTime(); + const mins = Math.floor(diff / 60000); + if (mins < 1) return "just now"; + if (mins < 60) return `${mins}m ago`; + const hours = Math.floor(mins / 60); + if (hours < 24) return `${hours}h ago`; + return `${Math.floor(hours / 24)}d ago`; +} + +function StatusBadge({ status }: { status: string }) { + const color = status === "LIVE" ? "#22C55E" : status === "STAGING" ? "#F59E0B" : "#A1A1AA"; + const label = status === "LIVE" ? "LIVE" : status === "STAGING" ? "STAGING" : "INACTIVE"; + const textColor = status === "LIVE" ? "#16A34A" : status === "STAGING" ? "#D97706" : "#71717A"; + return ( + + + {label} + + ); +} + +function AgentIconSmall() { + return ; +} + +function PersonIcon() { + return ; +} + +function OrgIcon() { + return ; +} + +function HoverTooltip({ label, items }: { label: string; items: string[] }) { + if (items.length === 0) return ; + return ( + + {label} + + {items.map((name, i) => {name})} + + + + ); +} + +type Tab = "agents" | "my-datasets" | "org-shared"; + +export default function ConnectionsPage() { + const { cogniInstance, isInitializing } = useCogniInstance(); + const { datasets: contextDatasets } = useFilter(); + const [agents, setAgents] = useState([]); + const [datasets, setDatasets] = useState(contextDatasets as Dataset[]); + const [loading, setLoading] = useState(true); + const [tab, setTab] = useState("agents"); + const [selectedAgentId, setSelectedAgentId] = useState(null); + const [showShareModal, setShowShareModal] = useState(false); + const [sharing, setSharing] = useState(false); + // agentId → datasetId → Set<"read"|"write"> + const [agentPermissions, setAgentPermissions] = useState>>>({});; + const [showCreateAgentModal, setShowCreateAgentModal] = useState(false); + const [createAgentName, setCreateAgentName] = useState(""); + const [creatingAgent, setCreatingAgent] = useState(false); + const [createdAgent, setCreatedAgent] = useState(null); + const [createAgentError, setCreateAgentError] = useState(null); + const [copiedKey, setCopiedKey] = useState(false); + const [showDeleteAgentModal, setShowDeleteAgentModal] = useState(false); + const [deletingAgent, setDeletingAgent] = useState(false); + const [allSessions, setAllSessions] = useState([]); + useEffect(() => { + if (!cogniInstance || isInitializing) return; + setDatasets(contextDatasets as Dataset[]); + Promise.all([ + cogniInstance.fetch("/v1/activity/agents").then((r) => r.ok ? r.json() : []).catch(() => []), + listSessions(cogniInstance, { range: "30d", limit: 100 }), + ]).then(async ([a, sessionsPage]) => { + setAllSessions(sessionsPage?.sessions ?? []); + const agentData = Array.isArray(a) ? a : []; + setAgents(agentData); + const firstAgent = agentData.find((x: Agent) => x.is_agent); + if (firstAgent) setSelectedAgentId(firstAgent.id); + + // OSS exposes grant/revoke through /v1/permissions/datasets but does + // not currently expose a per-agent dataset list endpoint. + const permMap: Record>> = {}; + setAgentPermissions(permMap); + }).finally(() => setLoading(false)); + }, [cogniInstance, isInitializing, contextDatasets]); + + // Keep local datasets in sync with FilterContext + useEffect(() => { + if (contextDatasets.length > 0) { + setDatasets(contextDatasets as Dataset[]); + } + }, [contextDatasets]); + + const agentUsers = agents.filter((a) => a.is_agent && !a.is_default); + const selectedAgent = agents.find((a) => a.id === selectedAgentId); + + // Agent's datasets (owned by agent OR shared with agent) + const agentPerms = selectedAgent ? (agentPermissions[selectedAgent.id] || {}) : {}; + const agentDatasets = selectedAgent + ? datasets.filter((d) => d.ownerId === selectedAgent.id || d.id in agentPerms) + : []; + + // My datasets (owned by default user) + const defaultUser = agents.find((a) => a.is_default); + const myDatasets = defaultUser + ? datasets.filter((d) => d.ownerId === defaultUser.id) + : datasets; + + // Reverse mapping: dataset ID → { agents: string[], users: string[] } + const datasetSharedWith = (() => { + const map: Record = {}; + const ensure = (id: string) => { if (!map[id]) map[id] = { agents: [], users: [] }; }; + for (const agent of agentUsers) { + const perms = agentPermissions[agent.id] || {}; + for (const dsId of Object.keys(perms)) { + ensure(dsId); + if (!map[dsId].agents.includes(agent.agent_type)) map[dsId].agents.push(agent.agent_type); + } + // Also include datasets owned by this agent + for (const ds of datasets) { + if (ds.ownerId === agent.id) { + ensure(ds.id); + if (!map[ds.id].agents.includes(agent.agent_type)) map[ds.id].agents.push(agent.agent_type); + } + } + } + // Track user (non-agent) owners + for (const ds of datasets) { + const owner = agents.find((a) => a.id === ds.ownerId); + if (owner && !owner.is_agent) { + ensure(ds.id); + const label = owner.is_default ? "You" : owner.email?.split("@")[0] || "User"; + if (!map[ds.id].users.includes(label)) map[ds.id].users.push(label); + } + } + return map; + })(); + + async function handleShareDataset(agentId: string, datasetId: string) { + if (!cogniInstance) return; + setSharing(true); + try { + await cogniInstance.fetch(`/v1/permissions/datasets/${agentId}?permission_name=read`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify([datasetId]), + }); + trackEvent({ pageName: "Connections", eventName: "dataset_shared_with_agent", additionalProperties: { dataset_id: datasetId, agent_id: agentId } }); + setAgentPermissions((prev) => { + const next = { ...prev }; + const agentPerms = { ...(prev[agentId] || {}) }; + agentPerms[datasetId] = new Set(["read"]); + next[agentId] = agentPerms; + return next; + }); + } catch (err) { + console.error("Share failed:", err); + } finally { + setSharing(false); + } + } + + async function handleCreateAgent() { + if (!cogniInstance || !createAgentName.trim()) return; + setCreatingAgent(true); + setCreateAgentError(null); + try { + const res = await cogniInstance.fetch(`/v1/agents/create?name=${encodeURIComponent(createAgentName.trim())}`, { method: "POST" }); + if (!res.ok) { + const body = await res.text(); + throw new Error(body || `Failed to create agent (${res.status})`); + } + const data: CreatedAgent = await res.json(); + setCreatedAgent(data); + trackEvent({ pageName: "Connections", eventName: "agent_created", additionalProperties: { agent_id: data.agentId, agent_email: data.agentEmail } }); + // Refresh agents list + cogniInstance.fetch("/v1/activity/agents").then((r) => r.ok ? r.json() : []).then((a) => { + const agentData = Array.isArray(a) ? a : []; + setAgents(agentData); + }).catch(() => {}); + } catch (err) { + setCreateAgentError(err instanceof Error ? err.message : "Failed to create agent"); + } finally { + setCreatingAgent(false); + } + } + + function closeCreateAgentModal() { + setShowCreateAgentModal(false); + setCreateAgentName(""); + setCreatedAgent(null); + setCreateAgentError(null); + setCopiedKey(false); + } + + if (loading || isInitializing) { + return <>
; + } + + const TABS: { id: Tab; label: string; icon: React.ReactNode }[] = [ + { id: "agents", label: "Agents", icon: }, + { id: "my-datasets", label: "My Datasets", icon: }, + { id: "org-shared", label: "Org Shared", icon: }, + ]; + + return ( +
+ {/* Header */} +
+
+

Connections

+ Manage agents, personal datasets, and shared organization data. +
+ { + event.preventDefault(); + setShowCreateAgentModal(true); + }} + className="hover:bg-cognee-purple-hover cursor-pointer" + style={{ background: "#6510F4", color: "#fff", border: "none", borderRadius: 8, padding: "8px 16px", fontSize: 13, fontWeight: 500, display: "flex", alignItems: "center", gap: 6, fontFamily: "inherit", flexShrink: 0 }} + > + + Create agent + +
+ + {/* Tabs */} +
+ {TABS.map((t) => ( + + ))} +
+ + {/* Share modal */} + {showShareModal && selectedAgent && (() => { + const currentPerms = agentPermissions[selectedAgent.id] || {}; + const sharedDs = datasets.filter((d) => d.id in currentPerms || d.ownerId === selectedAgent.id); + const unsharedDs = datasets.filter((d) => !(d.id in currentPerms) && d.ownerId !== selectedAgent.id); + return ( +
setShowShareModal(false)}> +
e.stopPropagation()} style={{ background: "#fff", borderRadius: 12, padding: 24, width: 480, maxHeight: "80vh", display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 16px 48px rgba(0,0,0,0.12)" }}> +

Manage dataset access

+

Share datasets with {selectedAgent.agent_type}.

+ + {/* Shared datasets */} +
+ Has access ({sharedDs.length}) +
+ {sharedDs.length === 0 ? ( +
No datasets shared yet
+ ) : ( +
+ {sharedDs.map((d) => ( +
+
+ {d.name} + {d.ownerId === selectedAgent.id ? "Owned" : "Read"} +
+ ))} +
+ )} +
+
+ + {/* Unshared datasets */} +
+ No access ({unsharedDs.length}) +
+ {unsharedDs.length === 0 ? ( +
All datasets are shared
+ ) : ( +
+ {unsharedDs.map((d) => ( +
+
+ {d.name} + +
+ ))} +
+ )} +
+
+ +
+ +
+
+
+ ); + })()} + + {/* Create Agent modal */} + {showCreateAgentModal && ( +
+
e.stopPropagation()} style={{ background: "#fff", borderRadius: 12, padding: 24, width: 480, display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 16px 48px rgba(0,0,0,0.12)" }}> + {!createdAgent ? ( + <> +

Create Agent

+

+ Give your agent a name. An API key will be generated automatically. +

+ setCreateAgentName(e.target.value)} + onKeyDown={(e) => { if (e.key === "Enter" && createAgentName.trim()) handleCreateAgent(); }} + autoFocus + style={{ border: "1px solid #E4E4E7", borderRadius: 8, padding: "10px 14px", fontSize: 14, fontFamily: "inherit", outline: "none" }} + /> + {createAgentError && ( + {createAgentError} + )} +
+ + +
+ + ) : ( + <> +

Agent Created

+
+ + + Copy the API key now. It will not be shown again. + +
+
+
+ Agent ID + {createdAgent.agentId} +
+
+ Agent Email + {createdAgent.agentEmail} +
+
+ API Key +
+ {createdAgent.agentApiKey} + +
+
+
+
+ +
+ + )} +
+
+ )} + + {/* Delete Agent modal */} + {showDeleteAgentModal && selectedAgent && ( +
!deletingAgent && setShowDeleteAgentModal(false)}> +
e.stopPropagation()} style={{ background: "#fff", borderRadius: 12, padding: 24, width: 420, display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 16px 48px rgba(0,0,0,0.12)" }}> +

Delete Agent

+

+ Are you sure you want to delete {selectedAgent.agent_type}? This will permanently remove the agent and revoke its API key. This action cannot be undone. +

+
+ + +
+
+
+ )} + + {/* Agents tab */} + {tab === "agents" && agentUsers.length === 0 && ( +
+ + No agents connected yet + Create an agent to get an API key, or follow the integration guides to set up a connection. +
+ { + event.preventDefault(); + setShowCreateAgentModal(true); + }} + className="cursor-pointer" + style={{ display: "inline-flex", alignItems: "center", gap: 6, background: "#6510F4", color: "#fff", border: "none", borderRadius: 8, padding: "8px 20px", fontSize: 13, fontWeight: 500, fontFamily: "inherit" }} + > + Create Agent + + + Integration Guides + +
+
+ )} + + {tab === "agents" && agentUsers.length > 0 && ( +
+ {/* Agent sidebar */} +
+ {agentUsers.map((a) => ( +
{ trackEvent({ pageName: "Connections", eventName: "connections_agent_selected", additionalProperties: { agent_id: a.id, agent_type: a.agent_type } }); setSelectedAgentId(a.id); }} + className="cursor-pointer" + style={{ + display: "flex", alignItems: "center", gap: 10, padding: "12px 16px", + borderRadius: 8, borderLeft: selectedAgentId === a.id ? "3px solid #6510F4" : "3px solid transparent", + background: selectedAgentId === a.id ? "#F0EDFF" : "transparent", + transition: "all 150ms", + }} + > + + {a.agent_type} +
+ ))} +
+ + {/* Agent detail */} +
+ {selectedAgent ? ( + <> +
+
+
+ {selectedAgent.agent_type} + +
+ + {agentDatasets.length} dataset{agentDatasets.length !== 1 ? "s" : ""} + {selectedAgent.last_active ? ` · Last active ${timeAgo(selectedAgent.last_active)}` : " · Never connected"} + +
+
+ + +
+
+ + {/* Agent activity metrics */} + s.user_id === selectedAgent.id)} /> + + {/* Agent datasets table */} +
+
+ Dataset + Owner + Access + Status +
+ {agentDatasets.length === 0 ? ( +
+ No datasets shared yet. Click "Share dataset" to grant access. +
+ ) : ( + agentDatasets.map((d, i) => { + const isOwned = selectedAgent && d.ownerId === selectedAgent.id; + return ( +
+
+
+ {d.name} +
+ {isOwned ? "Agent" : "Shared"} + {isOwned ? "Read & Write" : (() => { const p = agentPerms[d.id]; if (!p) return "Read"; const parts = []; if (p.has("read")) parts.push("Read"); if (p.has("write")) parts.push("Write"); return parts.join(" & ") || "Read"; })()} + Indexed +
+ ); + }) + )} +
+ + {/* Recent sessions */} + s.user_id === selectedAgent.id)} /> + + ) : ( + Select an agent to view details. + )} +
+
+ )} + + {/* My Datasets tab */} + {tab === "my-datasets" && ( +
+
+ My datasets + Datasets you own +
+
+
+ Name + Status + Shared +
+ {myDatasets.map((d, i) => ( +
+
+
+ {d.name} +
+ Indexed + + {(() => { + const s = datasetSharedWith[d.id]; + const count = s?.agents.length || 0; + if (count === 0) return ; + return ; + })()} + +
+ ))} + {myDatasets.length === 0 && ( +
No personal datasets.
+ )} +
+
+ )} + + {/* Org Shared tab */} + {tab === "org-shared" && ( +
+
+ Shared datasets + Visible to all members +
+
+
+ Name + Used by + Status +
+ {datasets.map((d, i) => { + const s = datasetSharedWith[d.id]; + const userCount = s?.users.length || 0; + const agentCount = s?.agents.length || 0; + const parts: string[] = []; + if (userCount > 0) parts.push(`${userCount} user${userCount !== 1 ? "s" : ""}`); + if (agentCount > 0) parts.push(`${agentCount} agent${agentCount !== 1 ? "s" : ""}`); + const allNames = [...(s?.users || []), ...(s?.agents || [])]; + return ( +
+
+
+ {d.name} +
+ + {parts.length > 0 + ? + : } + + Indexed +
+ ); + })} + {datasets.length === 0 && ( +
No shared datasets.
+ )} +
+
+ )} +
+ ); +} + +// ── Agent activity metrics ──────────────────────────────────────────── + +function AgentMetrics({ sessions }: { sessions: SessionRow[] }) { + if (sessions.length === 0) return null; + const total = sessions.length; + const completed = sessions.filter((s) => s.effective_status === "completed").length; + const failed = sessions.filter((s) => s.effective_status === "failed").length; + const successRate = total > 0 ? ((completed / (completed + failed || 1)) * 100) : 100; + const totalTokens = sessions.reduce((acc, s) => acc + s.tokens_in + s.tokens_out, 0); + const totalErrors = sessions.reduce((acc, s) => acc + s.error_count, 0); + + function fmt(n: number): string { + if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; + if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`; + return String(n); + } + + const metrics = [ + { label: "Sessions", value: String(total), color: "#18181B" }, + { label: "Success rate", value: `${successRate.toFixed(1)}%`, color: successRate >= 95 ? "#16A34A" : successRate >= 80 ? "#D97706" : "#DC2626" }, + { label: "Tokens", value: fmt(totalTokens), color: "#18181B" }, + { label: "Errors", value: String(totalErrors), color: totalErrors > 0 ? "#DC2626" : "#18181B" }, + ]; + + return ( +
+ {metrics.map((m) => ( +
+ {m.label} + {m.value} +
+ ))} +
+ ); +} + +// ── Agent session history ───────────────────────────────────────────── + +function AgentSessionHistory({ sessions }: { sessions: SessionRow[] }) { + const sorted = [...sessions].sort((a, b) => { + const ta = a.last_activity_at ? new Date(a.last_activity_at).getTime() : 0; + const tb = b.last_activity_at ? new Date(b.last_activity_at).getTime() : 0; + return tb - ta; + }); + const visible = sorted.slice(0, 10); + + if (visible.length === 0) { + return ( +
+ Recent Sessions +
+ No sessions recorded yet. +
+
+ ); + } + + const statusColor: Record = { running: "#3B82F6", completed: "#22C55E", failed: "#EF4444", abandoned: "#D97706" }; + + return ( +
+
+ Recent Sessions + {sessions.length > 10 && ( + + View all in Activity → + + )} +
+
+
+ Status + Session + Model + Tokens + Time +
+ {visible.map((s, i) => { + const status = s.effective_status || s.status || "unknown"; + const dot = statusColor[status] || "#A1A1AA"; + const tokens = s.tokens_in + s.tokens_out; + return ( + +
+ + {status} +
+ + {s.session_id.length > 24 ? `${s.session_id.slice(0, 24)}...` : s.session_id} + + {s.last_model ?? "—"} + + {tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens)} + + + {s.last_activity_at ? timeAgo(s.last_activity_at) : "—"} + + + ); + })} +
+
+ ); +} diff --git a/cognee-frontend/src/app/(app)/dashboard/DashboardSkeleton.tsx b/cognee-frontend/src/app/(app)/dashboard/DashboardSkeleton.tsx new file mode 100644 index 0000000..608c245 --- /dev/null +++ b/cognee-frontend/src/app/(app)/dashboard/DashboardSkeleton.tsx @@ -0,0 +1,107 @@ +"use client"; + +import SkeletonBar from "@/ui/elements/SkeletonBar"; + +const cardStyle: React.CSSProperties = { + border: "1px solid rgba(255,255,255,0.1)", + borderRadius: 12, + background: "rgba(255,255,255,0.03)", + padding: 16, + display: "flex", + flexDirection: "column", + gap: 12, + minHeight: 120, +}; + +function StatCard() { + return ( +
+ + +
+ ); +} + +function ConnectionCard() { + return ( +
+ + + +
+ ); +} + +export default function DashboardSkeleton() { + return ( +
+
+ + {/* Provisioning note */} +

+ Your workspace is being set up — usually takes under a minute. +

+ + {/* Greeting */} + + + {/* KPI strip */} +
+ {Array.from({ length: 6 }).map((_, i) => ( + + ))} +
+ + {/* Get started */} +
+
+
Get started
+
Connect your AI agents to give them persistent memory
+
+
+ {Array.from({ length: 4 }).map((_, i) => ( + + ))} +
+
+ + {/* Memory Activity */} +
+
+

Memory Activity

+

A live log of every search against your memory — by your agents and by you.

+
+
+ {Array.from({ length: 5 }).map((_, i) => ( + + ))} +
+
+ +
+
+ ); +} diff --git a/cognee-frontend/src/app/(app)/dashboard/OverviewPage.tsx b/cognee-frontend/src/app/(app)/dashboard/OverviewPage.tsx new file mode 100644 index 0000000..18f57f1 --- /dev/null +++ b/cognee-frontend/src/app/(app)/dashboard/OverviewPage.tsx @@ -0,0 +1,1596 @@ +"use client"; + +import React, { useEffect, useState, useRef } from "react"; +import { useRouter } from "next/navigation"; +import { useCogniInstance, useTenant } from "@/modules/tenant/TenantProvider"; +import getCreditsOverview from "@/modules/billing/getCreditsOverview"; +import { useFilter } from "@/ui/layout/FilterContext"; +import rememberData from "@/modules/ingestion/rememberData"; +import createDataset from "@/modules/datasets/createDataset"; +import pollDatasetStatus from "@/modules/datasets/pollDatasetStatus"; +import { loadGraphModelsConfig, findModelForDataset, findPromptForDataset, findOntologyForDataset } from "@/modules/configuration/userConfiguration"; +import { toCleanSchema } from "@/modules/graphModels/types"; +import { toGraphModelSchema } from "@/modules/graphModels/toGraphModelSchema"; +import { listSessions, SEARCH_SESSION_PREFIX } from "@/modules/sessions/getSessions"; +import getDatasetGraph from "@/modules/datasets/getDatasetGraph"; +import { notifications } from "@mantine/notifications"; +import { trackEvent, TrackPageView } from "@/modules/analytics"; +import { markOnboardingCompleteLocally } from "@/utils/onboardingFlag"; +import { CLAUDE_MARKETPLACE_ADD, CLAUDE_PLUGIN_INSTALL, CODEX_HOOKS_ENABLE, CODEX_MARKETPLACE_ADD, CODEX_PLUGIN_INSTALL, OPENCLAW_SKILL_INSTALL, GENERIC_SKILL_INSTALL, UPLOAD_MEMORY_PROMPT, UPLOAD_SAMPLE_PROMPT, RECALL_SAMPLE_PROMPT } from "@/data/prompts"; +import { AgentActivityTerminal, PipelineRun, Range, ownerDisplayName } from "@/ui/elements/AgentActivityTerminal"; +import SkeletonBar from "@/ui/elements/SkeletonBar"; +import DashboardSkeleton from "./DashboardSkeleton"; +import isCloudEnvironment from "@/utils/isCloudEnvironment"; +import { getLLMSettings, saveLLMApiKey, type LLMSettings } from "@/modules/settings/llmSettings"; + +const AWAITING_DATASET_KEY = "cognee-awaiting-dataset"; + +// Per-integration "Connected" detection. The Claude Code and Codex plugins mint +// their session_id as `{prefix}_{dir}_{token}` with a fixed prefix per agent +// (see topoteretes/cognee-integrations: claude-code → "cc", codex → "codex"), so +// we can tell which agent connected from the session list alone. Detection is +// coarse on purpose — any session whose id starts with the prefix counts. Openclaw +// forwards the host session id (no Cognee prefix) and API/MCP is user-defined, so +// neither is auto-detected. Keep these in sync with the shipped integrations +// (the plugins' _generate_session_id + the skill prompts in prompts.ts). +const INTEGRATION_SESSION_PREFIX: Record = { + "claude-code": "cc_", + codex: "codex_", +}; + +export default function OverviewPage() { + const { cogniInstance, isInitializing, serviceUrl, apiKey } = useCogniInstance(); + const { tenantReady, tenant, isOwner } = useTenant(); + const { agents, datasets, selectedDataset, selectedAgent, setSelectedDataset, refreshDatasets, loading: filterLoading } = useFilter(); + const [runs, setRuns] = useState([]); + const [loading, setLoading] = useState(true); + const [isUploading, setIsUploading] = useState(false); + const isHandlingUploadRef = useRef(false); + const [showDatasetPicker, setShowDatasetPicker] = useState(false); + const [pendingFiles, setPendingFiles] = useState([]); + const [range] = useState("24h"); + const [sessions, setSessions] = useState([]); + // Which integrations have ever connected (by session_id prefix). Sticky per + // tenant via localStorage so the badge survives the 24h session window. + const [connectedIntegrations, setConnectedIntegrations] = useState>({}); + const [graphNodes, setGraphNodes] = useState(() => { + try { const v = sessionStorage.getItem("cognee-graph-nodes"); return v !== null ? Number(v) : null; } catch { return null; } + }); + const [graphEdges, setGraphEdges] = useState(null); + const [showUploadDoneModal, setShowUploadDoneModal] = useState<{ datasetName: string; datasetId: string } | null>(null); + const uploadInputRef = useRef(null); + const sliderRef = useRef(null); + const sliderDragging = useRef(false); + const sliderStartX = useRef(0); + const sliderScrollLeft = useRef(0); + const [canScrollLeft, setCanScrollLeft] = useState(false); + const [canScrollRight, setCanScrollRight] = useState(false); + const [creditsBannerDismissed, setCreditsBannerDismissed] = useState(() => { + try { return sessionStorage.getItem("cognee-credits-banner-dismissed") === "1"; } catch { return false; } + }); + const [creditsSpentPct, setCreditsSpentPct] = useState(null); + const [creditsRemainingUsd, setCreditsRemainingUsd] = useState(null); + // Local/OSS only: whether the backend has an LLM API key configured. Without + // one, cognify fails on every upload, so the dashboard surfaces a banner + // with a paste-your-key modal that applies the key to the running backend. + const [llmSettings, setLlmSettings] = useState(null); + const [llmKeyMissing, setLlmKeyMissing] = useState(false); + const [showLlmKeyModal, setShowLlmKeyModal] = useState(false); + const [llmKeyInput, setLlmKeyInput] = useState(""); + const [llmKeySaving, setLlmKeySaving] = useState(false); + const [llmKeyError, setLlmKeyError] = useState(null); + // True while a freshly-provisioned default dataset (handed off from onboarding + // via sessionStorage) is still processing. Init from the flag so the skeleton + // shows on first paint without waiting for the effect below. + const [awaitingDataset, setAwaitingDataset] = useState(() => { + try { return !!sessionStorage.getItem(AWAITING_DATASET_KEY); } catch { return false; } + }); + // Gates the dashboard render until the onboarding-redirect check below has + // actually resolved. Without this, workspaceReady flips true (fast) before + // that async check finishes, so the full dashboard flashes on screen for a + // moment right before router.replace("/onboarding") kicks in. + const [onboardingDecided, setOnboardingDecided] = useState(false); + const router = useRouter(); + // Workspace is functionally ready only when the pod is up AND any in-flight + // default-dataset processing has finished. + const workspaceReady = !!cogniInstance && tenantReady && !awaitingDataset; + const prevWorkspaceReady = useRef(workspaceReady); + + useEffect(() => { + if (isCloudEnvironment() || !cogniInstance) return; + let cancelled = false; + getLLMSettings(cogniInstance) + .then((llm) => { + if (cancelled) return; + setLlmSettings(llm); + setLlmKeyMissing(!llm.apiKey); + }) + .catch(() => {}); + return () => { cancelled = true; }; + }, [cogniInstance]); + + async function handleSaveLlmKey() { + if (!cogniInstance || !llmKeyInput.trim() || llmKeySaving) return; + setLlmKeySaving(true); + setLlmKeyError(null); + try { + await saveLLMApiKey(cogniInstance, { + provider: llmSettings?.provider || "openai", + model: llmSettings?.model || "gpt-5-mini", + apiKey: llmKeyInput.trim(), + }); + trackEvent({ pageName: "Dashboard", eventName: "llm_api_key_saved" }); + // Reload so every widget starts from the now-working backend state. + window.location.reload(); + } catch (err) { + setLlmKeyError(err instanceof Error && err.message ? err.message : "Failed to save the API key. Is the backend running?"); + setLlmKeySaving(false); + } + } + + async function uploadToDataset(ds: { id: string; name: string }, files: File[]) { + if (!cogniInstance) return; + setIsUploading(true); + try { + // Load graph model, custom prompt, and ontology assignments for this dataset + const cfg = await loadGraphModelsConfig(cogniInstance); + const rememberOpts: { graphModel?: object; customPrompt?: string; ontologyKey?: string[] } = {}; + const assignedModel = findModelForDataset(cfg.models, ds.id); + if (assignedModel) { + const cleanSchema = toCleanSchema(assignedModel.schema); + rememberOpts.graphModel = toGraphModelSchema(cleanSchema); + } + const promptName = findPromptForDataset(cfg.promptAssignments ?? {}, ds.id); + if (promptName && cfg.customPrompts?.[promptName]) { + rememberOpts.customPrompt = cfg.customPrompts[promptName]; + } + const ontologyKey = findOntologyForDataset(cfg.ontologyAssignments ?? {}, ds.id); + if (ontologyKey) { + rememberOpts.ontologyKey = [ontologyKey]; + } + await rememberData({ id: ds.id }, files, cogniInstance, rememberOpts); + trackEvent({ pageName: "Dashboard", eventName: "dashboard_files_uploaded", additionalProperties: { dataset_id: ds.id, dataset_name: ds.name, file_count: String(files.length) } }); + notifications.show({ title: `Files uploaded to "${ds.name}"`, message: `${files.length} file(s) added. Cognify running.`, color: "blue", autoClose: 5000 }); + await pollDatasetStatus(ds.id, cogniInstance, { intervalMs: 5000 }); + refreshDatasets(); + setShowUploadDoneModal({ datasetName: ds.name, datasetId: ds.id }); + } catch (err) { + console.error("Dashboard upload failed:", err); + notifications.show({ title: "Upload failed", message: err instanceof Error ? err.message : String(err), color: "red" }); + } finally { + setIsUploading(false); + } + } + + async function handleDashboardUpload(e: React.ChangeEvent) { + // Ref (not state) so the re-entrancy check is synchronous — guards against + // a stray double-fired change event racing two dataset-creation calls for + // the same deterministic dataset id, which would hit a UNIQUE constraint + // on the backend (create_new_dataset's existence check + insert isn't atomic). + if (isHandlingUploadRef.current || !cogniInstance || !e.target.files?.length) return; + isHandlingUploadRef.current = true; + const files = Array.from(e.target.files); + e.target.value = ""; + + try { + // If only one dataset exists, upload to it + if (datasets.length === 1) { + await uploadToDataset(datasets[0], files); + return; + } + + // If no datasets exist, create default and upload + if (datasets.length === 0) { + const ds = await createDataset({ name: "default_dataset" }, cogniInstance); + refreshDatasets(); + await uploadToDataset(ds, files); + return; + } + + // Multiple datasets, none selected — show picker + setPendingFiles(files); + setShowDatasetPicker(true); + } catch (err) { + console.error("Dashboard upload failed:", err); + notifications.show({ title: "Upload failed", message: err instanceof Error ? err.message : String(err), color: "red" }); + } finally { + isHandlingUploadRef.current = false; + } + } + + async function handlePickDataset(ds: { id: string; name: string }) { + setShowDatasetPicker(false); + setSelectedDataset(ds); + trackEvent({ pageName: "Dashboard", eventName: "dashboard_dataset_picked", additionalProperties: { dataset_id: ds.id, dataset_name: ds.name } }); + await uploadToDataset(ds, pendingFiles); + setPendingFiles([]); + } + + useEffect(() => { + if (!cogniInstance || isInitializing || filterLoading) return; + + let cancelled = false; + + // Poll telemetry + sessions every 15s. The Activity & Memory + // table binds to `sessions`, so each tick refreshes it. + function fetchTelemetry() { + return Promise.all([ + cogniInstance! + .fetch("/v1/activity/pipeline-runs") + .then((r) => (r.ok ? r.json() : [])) + .catch(() => []), + listSessions(cogniInstance!, { range, limit: 50 }), + ]).then(([runData, sessionsPage]) => { + if (cancelled) return; + setRuns(Array.isArray(runData) ? runData : []); + setSessions(sessionsPage?.sessions ?? []); + }); + } + + fetchTelemetry() + .then(() => { + if (cancelled) return; + }) + .finally(() => { + if (!cancelled) setLoading(false); + }); + + const interval = setInterval(fetchTelemetry, 15000); + return () => { + cancelled = true; + clearInterval(interval); + }; + }, [cogniInstance, isInitializing, filterLoading, range]); + + // Derive per-integration "Connected" state from the session_id prefixes, and + // persist it per tenant so a card stays "Connected" after its session ages out + // of the polled window. Hydrates from localStorage and merges in live matches. + const tenantId = tenant?.tenant_id ?? null; + useEffect(() => { + if (!tenantId) return; + const storeKey = `cognee-connected-integrations-${tenantId}`; + let persisted: Record = {}; + try { persisted = JSON.parse(localStorage.getItem(storeKey) || "{}"); } catch { /* ignore */ } + const next = { ...persisted }; + for (const [key, prefix] of Object.entries(INTEGRATION_SESSION_PREFIX)) { + if (sessions.some((s) => s.session_id.startsWith(prefix))) next[key] = true; + } + if (JSON.stringify(next) !== JSON.stringify(persisted)) { + try { localStorage.setItem(storeKey, JSON.stringify(next)); } catch { /* ignore */ } + } + setConnectedIntegrations((prev) => (JSON.stringify(prev) !== JSON.stringify(next) ? next : prev)); + }, [sessions, tenantId]); + + // Fetch graph node/edge counts whenever datasets or selected brain changes + useEffect(() => { + if (!cogniInstance || !datasets.length) return; + const datasetsToFetch = selectedDataset ? datasets.filter(d => d.id === selectedDataset.id) : datasets; + if (!datasetsToFetch.length) { setGraphNodes(0); setGraphEdges(0); return; } + let cancelled = false; + Promise.all( + datasetsToFetch.map((ds) => getDatasetGraph(ds, cogniInstance).catch(() => null)) + ).then((graphs) => { + if (cancelled) return; + let totalNodes = 0; + let totalEdges = 0; + for (const g of graphs) { + if (g && Array.isArray(g.nodes)) totalNodes += g.nodes.length; + if (g && Array.isArray(g.edges)) totalEdges += g.edges.length; + } + setGraphNodes(totalNodes); + setGraphEdges(totalEdges); + try { sessionStorage.setItem("cognee-graph-nodes", String(totalNodes)); } catch {} + }).catch(() => {}); + return () => { cancelled = true; }; + }, [cogniInstance, datasets, selectedDataset]); + + // Track whether the "What you can build" slider can scroll left/right, so + // we can show/hide the edge fades + arrow buttons accordingly. + useEffect(() => { + const el = sliderRef.current; + if (!el) return; + const update = () => { + setCanScrollLeft(el.scrollLeft > 4); + setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 4); + }; + update(); + el.addEventListener("scroll", update, { passive: true }); + const ro = new ResizeObserver(update); + ro.observe(el); + return () => { el.removeEventListener("scroll", update); ro.disconnect(); }; + }, []); + + function scrollSlider(delta: number) { + sliderRef.current?.scrollBy({ left: delta, behavior: "smooth" }); + } + + // Onboarding redirect: send fresh users to onboarding until they have pipeline activity. + // First-login case (no cogniInstance yet because the tenant is still being + // provisioned in the background) redirects immediately — the data-fetch + // effect above doesn't fire without an instance, so `loading` would + // otherwise stay true forever. + // Notify user when workspace becomes functionally ready (false→true transition). + useEffect(() => { + if (!prevWorkspaceReady.current && workspaceReady) { + notifications.show({ + title: "Your workspace is ready", + message: "All features are now available.", + color: "teal", + autoClose: 5000, + }); + } + prevWorkspaceReady.current = workspaceReady; + }, [workspaceReady]); + + // Wait for a freshly-provisioned default dataset (handed off from onboarding) + // to finish processing, then clear the flag. Best-effort: any error or a + // missing dataset resolves to "ready" so the UI is never blocked indefinitely. + useEffect(() => { + let datasetId: string | null = null; + try { datasetId = sessionStorage.getItem(AWAITING_DATASET_KEY); } catch { /* ignore */ } + if (!datasetId) return; + + let cancelled = false; + const clear = () => { + if (cancelled) return; + try { sessionStorage.removeItem(AWAITING_DATASET_KEY); } catch { /* ignore */ } + setAwaitingDataset(false); + }; + + // Safety net: never block the dashboard longer than 30s regardless of pod state. + // The polling below also has its own error handling, but if cogniInstance is null + // (pod still starting) the poll never runs — this timeout prevents that deadlock. + const safetyTimeout = setTimeout(clear, 30000); + + if (!cogniInstance) { + return () => { cancelled = true; clearTimeout(safetyTimeout); }; + } + + pollDatasetStatus(datasetId, cogniInstance, { intervalMs: 5000 }) + .then(clear) + .catch(clear); + + return () => { cancelled = true; clearTimeout(safetyTimeout); }; + }, [cogniInstance]); + + // Fetch credit usage for the low-balance warning banner. Not gated on + // dismissal: the below-$1 red banner must show even after the percentage + // banner is dismissed. + useEffect(() => { + if (!tenant) return; + getCreditsOverview().then((ov) => { + if (!ov) return; + const t = ov.tenants.find((t) => t.tenantId === tenant.tenant_id); + if (!t) return; + if (t.spentUsd != null && t.maxBudgetUsd) { + setCreditsSpentPct(Math.round((t.spentUsd / t.maxBudgetUsd) * 100)); + } + if (t.remainingUsd != null) { + setCreditsRemainingUsd(t.remainingUsd); + } + }).catch(() => {}); + }, [isOwner, tenant]); + + // Onboarding redirect: check Auth0 app state, then verify user actually has + // datasets or runs before trusting onboarding_complete. The flag can be set + // incorrectly by the backfill path, so we double-check with real data. + useEffect(() => { + // tenant === null means new user is in the welcome/provisioning flow — TenantProvider + // will redirect to /welcome. Don't race it with an /onboarding redirect. + if (isInitializing || !tenant) return; + // Don't redirect until both data fetches have settled. + if (loading || filterLoading) return; + + let cancelled = false; + (async () => { + const localSkipped = sessionStorage.getItem("cognee-onboarding-skipped"); + if (localSkipped) { + setOnboardingDecided(true); + return; + } + + // Fast path: if user has runs OR datasets they've genuinely onboarded. + if (runs.length > 0 || datasets.length > 0) { + markOnboardingCompleteLocally(); + setOnboardingDecided(true); + return; + } + + // No local activity — verify via Auth0 before redirecting. + // onboarding_complete is only trusted here when there's NO activity + // (datasets + runs both empty), which means we should redirect unless + // the flag was legitimately set AND the pod is still warming up. + // Gate on tenantReady so we don't redirect during pod cold-start. + if (!tenantReady) return; + + try { + const res = await fetch("/api/user-app-state"); + if (cancelled) return; + const appState = res.ok ? await res.json() : null; + if (appState?.onboarding_complete) { + // Flag is set but user has zero activity — could be a new workspace + // that hasn't finished provisioning, or a genuinely empty account. + // Re-check localStorage to see if this session already saw activity. + const localComplete = localStorage.getItem("cognee-onboarding-complete"); + if (localComplete) { + setOnboardingDecided(true); + return; // already validated this session + } + // Otherwise redirect — the onboarding flow will handle re-entry gracefully. + } + } catch { /* fallback to redirect below */ } + + if (cancelled) return; + // Deliberately leave onboardingDecided false here — we're navigating + // away, so the skeleton should stay up through the route change + // instead of flashing the dashboard for a frame first. + router.replace("/onboarding"); + })(); + return () => { cancelled = true; }; + }, [cogniInstance, tenantReady, isInitializing, loading, filterLoading, runs, datasets, router, tenant]); + + // Show skeleton until pod is up (cogniInstance + tenantReady), any + // freshly-provisioned default dataset has finished processing, AND the + // onboarding-redirect check above has resolved (see onboardingDecided). + // Workspace still provisioning: we already know this user is staying on + // the dashboard, so the dashboard-shaped skeleton communicates progress. + if (!workspaceReady) { + return ; + } + // Onboarding decision still pending: this user might get redirected to + // /onboarding in a moment, so deliberately show a neutral, non-dashboard + // loading state here instead of DashboardSkeleton — otherwise the + // dashboard-shaped skeleton itself reads as "the dashboard flashed" even + // though no real data was ever rendered. + if (!onboardingDecided) { + return ( +
+
+ +
+ ); + } + + const dataLoading = loading || isInitializing || filterLoading; + + // Deduplicate runs + const latestRuns: PipelineRun[] = []; + const seen = new Set(); + for (const r of runs) { + const key = r.pipeline_run_id || r.id; + if (!seen.has(key)) { seen.add(key); latestRuns.push(r); } + } + + const filteredRuns = latestRuns; + const filteredDatasets = selectedDataset ? datasets.filter(d => d.id === selectedDataset.id) : datasets; + + const apiCalls = filteredRuns.length; + const connectedAgents = agents.filter((a) => a.is_agent && !a.is_default); + const liveAgentIds = new Set( + sessions.filter((s) => s.effective_status === "running").map((s) => s.user_id) + ); + const liveAgents = connectedAgents.filter((a) => liveAgentIds.has(a.id) || a.status === "LIVE"); + const sessionCount = sessions.length; + + const greeting = greetingForTime(); + + // Only one banner may show at a time. Priority: the percentage low-credit / + // out-of-credits banner wins, then the below-$1 balance banner, then the + // promotional voucher banner. The below-$1 banner still resurfaces once the + // percentage banner is dismissed, so an out-of-credits workspace is never + // left without a warning. + const showCreditPctBanner = + !creditsBannerDismissed && creditsSpentPct !== null && creditsSpentPct >= 90; + const showLowBalanceBanner = + !showCreditPctBanner && creditsRemainingUsd !== null && creditsRemainingUsd < 1; + // Redeeming a voucher for cloud credits is meaningless in a self-hosted + // install (there's no billing backend to redeem against), so this banner + // — which would otherwise be the default fallback whenever the other two + // credit banners are inactive (i.e. always, in OSS mode) — is cloud-only. + const showVoucherBanner = isCloudEnvironment() && !showCreditPctBanner && !showLowBalanceBanner; + + return ( +
+ {/* Hidden file input for dashboard upload */} + + + {/* Dataset picker modal */} + {showDatasetPicker && ( +
{ setShowDatasetPicker(false); setPendingFiles([]); }}> +
e.stopPropagation()} style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 12, padding: 24, width: 420, maxWidth: "calc(100vw - 32px)", display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 20px 60px rgba(0,0,0,0.6)" }}> +

Upload to which brain?

+

+ {pendingFiles.length} file{pendingFiles.length !== 1 ? "s" : ""} selected. Choose a brain to upload to. +

+
+ {datasets.map((ds) => ( + + ))} +
+
+ +
+
+
+ )} + + + +
+ + {/* LLM API key modal (local mode only) */} + {showLlmKeyModal && ( +
{ if (!llmKeySaving) setShowLlmKeyModal(false); }}> +
e.stopPropagation()} style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 12, padding: 24, width: 420, maxWidth: "calc(100vw - 32px)", display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 20px 60px rgba(0,0,0,0.6)" }}> +

Add your LLM API key

+

+ Cognee uses {llmSettings?.provider || "openai"} ({llmSettings?.model || "gpt-5-mini"}) to build your knowledge graph. Paste your API key — it's applied to the running backend right away. +

+ { setLlmKeyInput(e.target.value); setLlmKeyError(null); }} + onKeyDown={(e) => { if (e.key === "Enter") handleSaveLlmKey(); }} + placeholder="sk-…" + autoFocus + disabled={llmKeySaving} + style={{ background: "rgba(255,255,255,0.06)", border: "1px solid rgba(255,255,255,0.15)", borderRadius: 8, padding: "10px 12px", fontSize: 13, color: "#EDECEA", fontFamily: "inherit", outline: "none", width: "100%", boxSizing: "border-box" }} + /> + {llmKeyError && ( +

{llmKeyError}

+ )} +
+ + +
+
+
+ )} + + {/* Compact greeting */} +
+

+ {greeting}{selectedAgent ? `, ${ownerDisplayName(selectedAgent.email)}` : ""} +

+ {selectedAgent && ( + {selectedAgent.agent_type} + )} +
+ + {/* ── Missing LLM API key banner (local mode only) ─────────────────── */} + {llmKeyMissing && ( +
+
+ + + + + + No LLM API key configured — Cognee can't process uploads until you add one. + +
+ +
+ )} + + {/* ── Low-credit warning banner ────────────────────────────────────── */} + {showCreditPctBanner && ( +
= 100 ? "rgba(239,68,68,0.10)" : "rgba(234,179,8,0.10)", + border: `1px solid ${creditsSpentPct >= 100 ? "rgba(239,68,68,0.30)" : "rgba(234,179,8,0.30)"}`, + borderRadius: 10, padding: "12px 16px", + }}> +
+ = 100 ? "#EF4444" : "#EAB308"} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> + + + + = 100 ? "#FCA5A5" : "#FDE047" }}> + {creditsSpentPct >= 100 + ? "Your workspace has used all available credits — agent requests may fail." + : `Your workspace has used ${creditsSpentPct}% of available credits.`} + +
+
+ {isOwner ? ( + = 100 ? "#FCA5A5" : "#FDE047", textDecoration: "underline", textUnderlineOffset: 3 }}> + Top up credits → + + ) : ( + Ask the workspace owner to top up. + )} + +
+
+ )} + + {/* ── Voucher banner / low-balance red banner ──────────────────────── */} + {showLowBalanceBanner ? ( +
+
+ + + + + + Your Token Balance is below 1 USD. You can recharge credits{" "} + + here + + . + +
+
+ ) : showVoucherBanner ? ( +
+
+ + + + + + You have a voucher? Redeem it here. + +
+ + Redeem voucher → + +
+ ) : null} + + {/* ── KPI strip ────────────────────────────────────────────────────── */} + + + {/* Agent + brain connection cards */} +
+
+
Get started
+
Connect your AI agents to give them persistent memory
+
+ uploadInputRef.current?.click()} + isUploading={isUploading} + serviceUrl={serviceUrl} + apiKey={apiKey} + isInitializing={isInitializing} + hasDocuments={datasets.length > 0} + cogniInstance={cogniInstance} + integrationConnected={connectedIntegrations} + /> +
+ + {/* ── HERO: Agent memory terminal ─────────────────────────────────── */} +
+ + {/* Section header */} +
+

Memory Activity

+

A live log of every search against your memory — by your agents and by you. Click any row to see what was searched and why it answered.

+
+ + router.push(path)} + /> +
+ + {/* ── Use-case cards — infinite slider ─────────────────────────── */} +
+
+

What you can build

+

Persistent memory and knowledge graphs for any domain

+
+ + + + {/* Drag-to-scroll slider with edge fades + arrow affordances. + Fades only appear when there's actually more content in that + direction — otherwise the leftmost card's left border would be + occluded by a constant fade. */} +
+ {canScrollLeft && ( +
+ )} + {canScrollRight && ( +
+ )} + {canScrollLeft && ( + + )} + {canScrollRight && ( + + )} + +
{ + sliderDragging.current = true; + sliderStartX.current = e.pageX - (sliderRef.current?.offsetLeft ?? 0); + sliderScrollLeft.current = sliderRef.current?.scrollLeft ?? 0; + sliderRef.current?.classList.add("is-dragging"); + }} + onMouseMove={(e) => { + if (!sliderDragging.current || !sliderRef.current) return; + e.preventDefault(); + const x = e.pageX - sliderRef.current.offsetLeft; + sliderRef.current.scrollLeft = sliderScrollLeft.current - (x - sliderStartX.current) * 1.2; + }} + onMouseUp={() => { sliderDragging.current = false; sliderRef.current?.classList.remove("is-dragging"); }} + onMouseLeave={() => { sliderDragging.current = false; sliderRef.current?.classList.remove("is-dragging"); }} + style={{ display: "flex", gap: 16, padding: "4px 0 8px" }} + > + {([ + "A second brain", + "Sales & deal intelligence", + "Investment & research", + "Docs & manuals", + "Memory for coding agents", + ]).map((title) => ( + { if (sliderRef.current && sliderRef.current.scrollLeft !== sliderScrollLeft.current) e.preventDefault(); }} + > + + {title} + + + ))} +
+
+
+ + {/* Upload done modal */} + {showUploadDoneModal && ( +
setShowUploadDoneModal(null)}> +
e.stopPropagation()} style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 12, padding: 28, width: 440, maxWidth: "calc(100vw - 32px)", display: "flex", flexDirection: "column", gap: 20, boxShadow: "0 20px 60px rgba(0,0,0,0.6)" }}> +
+
+ +
+
+

Knowledge graph built

+

“{showUploadDoneModal.datasetName}” is now searchable.

+
+
+
+ + + +
+ +
+
+ )} + +
{/* end content zIndex:3 wrapper */} +
+ ); +} + +// ── Helpers ────────────────────────────────────────────────────────────── + +function greetingForTime(): string { + const h = new Date().getHours(); + if (h < 12) return "Good morning"; + if (h < 18) return "Good afternoon"; + return "Good evening"; +} + +// ── Sub-components ─────────────────────────────────────────────────────── + +function CompactStatsStrip({ + liveAgents, apiCalls, sessionCount, graphNodes, graphEdges, brains, dataLoading, +}: { + liveAgents: number; apiCalls: number; sessionCount: number; + graphNodes: number | null; graphEdges: number | null; + brains: number; range: Range; dataLoading: boolean; +}) { + // Graph counts arrive on a separate async path (null until fetched). + // Gate each metric on ALL inputs needed for its final value being ready, + // so we go skeleton → final number ONCE, no intermediate 0 or "—". + const graphLoading = dataLoading || graphNodes === null || graphEdges === null; + const metrics: { label: string; value: number; loading: boolean; skeletonWidth: number }[] = [ + { + // "Active Agents" — agents with a session currently running. Label stays "Agents". + label: "Agents", + value: liveAgents, + loading: dataLoading, + skeletonWidth: 24, + }, + { + label: "Sessions", + value: sessionCount, + loading: dataLoading, + skeletonWidth: 36, + }, + { + label: "API calls", + value: apiCalls, + loading: dataLoading, + skeletonWidth: 36, + }, + { + label: "Graph nodes", + value: graphNodes ?? 0, + loading: graphLoading, + skeletonWidth: 48, + }, + { + label: "Graph edges", + value: graphEdges ?? 0, + loading: graphLoading, + skeletonWidth: 48, + }, + { + label: "Brains", + value: brains, + loading: dataLoading, + skeletonWidth: 20, + }, + ]; + + return ( +
+
+ {metrics.map((m, i) => { + const isZero = !m.loading && m.value === 0; + return ( +
+
+ {m.label} + + {m.loading ? : m.value.toLocaleString()} + +
+ {i < metrics.length - 1 && ( +
+ )} +
+ ); + })} +
+
+ ); +} + +// ── AgentConnectionSection (popup modal flow) ──────────────────────────── + +// ── Skill copy block ────────────────────────────────────────────────────── + +function SkillCopyBlock({ path, content, card }: { path: string; content: string; card?: AciAgentKey }) { + const [phase, setPhase] = useState<"idle" | "copying" | "done">("idle"); + + function handleCopy(e: React.MouseEvent) { + e.stopPropagation(); + trackEvent({ pageName: "Dashboard", eventName: "agent_config_copied", additionalProperties: { card: card ?? "unknown", block: "skill_install" } }); + navigator.clipboard.writeText(content); + setPhase("copying"); + setTimeout(() => setPhase("done"), 900); + setTimeout(() => setPhase("idle"), 3800); + } + + return ( +
e.stopPropagation()}> + {/* Destination path — purely informational, nothing runs here. Mono grey + to match the other code snippets in the modal (InlineCodeBlock uses + the same 0.85 alpha). */} +
+ + + + {path} +
+ + {/* Copy button — solid lavender so it reads as the primary action. */} + + + {/* Shows what the user will paste — runs entirely on their machine, not ours */} + {phase === "done" && ( +
+
$ paste & run the command in your terminal
+
↳ writes {path} on your local machine
+
+ )} +
+ ); +} + +function InlineCodeBlock({ code, toCopy, loading, card, block }: { code: string; toCopy?: string; loading?: boolean; card?: AciAgentKey; block?: string }) { + const [copied, setCopied] = useState(false); + function doCopy() { + if (loading) return; + trackEvent({ pageName: "Dashboard", eventName: "agent_config_copied", additionalProperties: { card: card ?? "unknown", block: block ?? "code" } }); + navigator.clipboard.writeText(toCopy ?? code); + setCopied(true); + setTimeout(() => setCopied(false), 1800); + } + return ( +
{ e.stopPropagation(); doCopy(); }} + style={{ background: "#18181B", borderRadius: 8, padding: "11px 14px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, cursor: loading ? "wait" : "pointer" }} + > +
+        {loading ? "Loading…" : code}
+      
+ +
+ ); +} + +interface AciStepDef { + title: string; + description: string; + code?: string; + codeToCopy?: string; + loading?: boolean; + /** When set, renders multiple separately-copyable code blocks (e.g. commands run one at a time) */ + codeBlocks?: { code: string; toCopy?: string; label?: string }[]; + /** When set, renders a SkillCopyBlock instead of (or alongside) the code block */ + skillPath?: string; + skillContent?: string; +} + +type AciAgentKey = "upload" | "claude-code" | "codex" | "openclaw" | "api-mcp"; + +function AgentConnectionSection({ + onUploadClick, isUploading, serviceUrl, apiKey, isInitializing, hasDocuments, cogniInstance, integrationConnected = {}, +}: { + onUploadClick: () => void; isUploading: boolean; + serviceUrl: string | null; apiKey: string | null; isInitializing: boolean; + hasDocuments: boolean; + cogniInstance: ReturnType["cogniInstance"]; + integrationConnected?: Record; +}) { + const router = useRouter(); + const [activeKey, setActiveKey] = useState(null); + const [stepIndexMap, setStepIndexMap] = useState>>({}); + // Live connection check for the Claude Code flow: while the modal is open we + // poll for new sessions. A session that wasn't present when the modal opened + // means the user's agent successfully connected to Cognee Cloud — we then mark + // the "connect" step done and jump to the final step. Index of the connect + // step within the claude-code steps (creds, plugin, → connect, review). + const CLAUDE_CONNECT_STEP = 2; + const [connectVerified, setConnectVerified] = useState(false); + + const baseUrl = serviceUrl || "https://your-tenant.aws.cognee.ai"; + const resolvedKey = apiKey || "your-api-key"; + // Cognee Cloud standardizes on COGNEE_BASE_URL everywhere user-facing — the + // plugins/skills (claude-code, codex, …) and the REST flows all read it. + const CREDS_CODE = `export COGNEE_BASE_URL="${baseUrl}"\nexport COGNEE_API_KEY="${resolvedKey}"`; + + // Poll for a freshly-created session while the Claude Code modal is open. + // Baseline the existing session ids on open; the first new (non-search-ui) + // session that appears flips the connect step to "connected". + useEffect(() => { + if (activeKey !== "claude-code" || !cogniInstance || connectVerified) return; + let cancelled = false; + const baseline = new Set(); + let primed = false; + + const realSessionIds = (rows: { session_id: string }[]) => + rows.map((s) => s.session_id).filter((id) => !id.startsWith(SEARCH_SESSION_PREFIX)); + + async function check() { + const page = await listSessions(cogniInstance!, { range: "24h", limit: 50 }); + if (cancelled) return; + const ids = realSessionIds(page.sessions); + if (!primed) { + // First tick establishes the baseline of pre-existing sessions. + ids.forEach((id) => baseline.add(id)); + primed = true; + return; + } + if (ids.some((id) => !baseline.has(id))) { + setConnectVerified(true); + // Mark the connect step done and reveal the final "review" step. + setStepIndexMap((prev) => { + const cur = prev["claude-code"] ?? 0; + return cur <= CLAUDE_CONNECT_STEP ? { ...prev, "claude-code": CLAUDE_CONNECT_STEP + 1 } : prev; + }); + } + } + + check(); + const id = setInterval(check, 7000); + return () => { cancelled = true; clearInterval(id); }; + }, [activeKey, cogniInstance, connectVerified]); + + // Reset the verified flag whenever the Claude Code modal is (re)opened. + useEffect(() => { + if (activeKey === "claude-code") setConnectVerified(false); + }, [activeKey]); + + const CARDS_CFG: { key: AciAgentKey; name: string; description: string }[] = [ + { key: "claude-code", name: "Claude Code", description: "Give Claude Code persistent memory across all your projects" }, + { key: "codex", name: "Codex", description: "Connect OpenAI Codex to your knowledge graph via the Cognee plugin" }, + { key: "openclaw", name: "Openclaw", description: "Connect Openclaw to your knowledge graph via AGENTS.md" }, + { key: "api-mcp", name: "API / MCP", description: "Connect any agent or app via the REST API or MCP" }, + { key: "upload", name: "Company Brain", description: "Upload PDFs, docs, and data to build your knowledge graph" }, + ]; + + function getSteps(key: AciAgentKey): AciStepDef[] { + const credStep: AciStepDef = { + title: "Set your API credentials", + description: "Open a terminal and run these commands to configure your Cognee endpoint and key.", + code: `export COGNEE_BASE_URL="${baseUrl}"`, + codeToCopy: CREDS_CODE, + loading: isInitializing, + }; + if (key === "claude-code") return [ + credStep, + { + title: "Install the Cognee plugin", + description: "Run these in your terminal one at a time — register the Cognee marketplace, then install the memory plugin.", + codeBlocks: [ + { code: CLAUDE_MARKETPLACE_ADD }, + { code: CLAUDE_PLUGIN_INSTALL }, + ], + }, + { + title: "Upload something to Cognee", + description: "Pick one and paste it into Claude — it stores the content in your Cognee memory so you can recall it in the next step.", + codeBlocks: [ + { label: "Option A · Your existing memory", code: UPLOAD_MEMORY_PROMPT }, + { label: "Option B · Try it with a sample", code: UPLOAD_SAMPLE_PROMPT }, + ], + }, + { + title: connectVerified ? "Connected — session detected ✓" : "Recall it from Cognee", + description: connectVerified + ? "We detected your new session in Cognee Cloud — you're connected." + : "First run /exit to close the session — that syncs it into Cognee Cloud — then reopen Claude Code and ask the question below. Answering from a fresh session proves it's recalling from your cloud memory.", + codeBlocks: [ + { code: "/exit" }, + { code: RECALL_SAMPLE_PROMPT }, + ], + }, + { + title: "You're all set", + description: "The Cognee plugin hooks into Claude Code's lifecycle — no curl or manual API calls — and captures your session as you work. When a session ends (e.g. /exit), it consolidates that session into your Cognee Cloud knowledge graph, and every new session automatically recalls it back. Sessions are disposable; your memory isn't.", + }, + ]; + if (key === "codex") return [ + credStep, + { + title: "Install the Cognee plugin", + description: "Run these in your terminal one at a time — enable Codex hooks, register the Cognee marketplace, then install the memory plugin.", + codeBlocks: [ + { code: CODEX_HOOKS_ENABLE }, + { code: CODEX_MARKETPLACE_ADD }, + { code: CODEX_PLUGIN_INSTALL }, + ], + }, + { + title: "Upload something to Cognee", + description: "Pick one and paste it into Codex — it stores the content in your Cognee memory so you can recall it in the next step.", + codeBlocks: [ + { label: "Option A · Your existing memory", code: UPLOAD_MEMORY_PROMPT }, + { label: "Option B · Try it with a sample", code: UPLOAD_SAMPLE_PROMPT }, + ], + }, + { + title: "Recall it from Cognee", + description: "First run /exit to close the session — that syncs it into Cognee Cloud — then reopen Codex and ask the question below. Answering from a fresh session proves it's recalling from your cloud memory.", + codeBlocks: [ + { code: "/exit" }, + { code: RECALL_SAMPLE_PROMPT }, + ], + }, + { + title: "You're all set", + description: "The Cognee plugin hooks into Codex's lifecycle — no curl or manual API calls — and captures your session as you work. When a session ends (e.g. /exit), it consolidates that session into your Cognee Cloud knowledge graph, and every new session automatically recalls it back. Sessions are disposable; your memory isn't.", + }, + ]; + if (key === "openclaw") return [ + credStep, + { + title: "Install the Cognee skill", + description: "Click below to copy the install command to your clipboard, then paste and run it in your local terminal. Nothing is sent to our servers — the skill file is written on your own machine.", + skillPath: "~/.openclaw/skills/cognee/SKILL.md", + skillContent: OPENCLAW_SKILL_INSTALL, + }, + { + title: "Test the connection", + description: `Open Openclaw in your project and ask: "What do you know from cognee?" — if it responds with knowledge from your brain, you're connected.`, + }, + ]; + if (key === "api-mcp") return [ + credStep, + { + title: "Query the REST API", + description: "Send a recall query to your Cognee endpoint from any HTTP client or language.", + code: `curl -X POST ${baseUrl}/api/v1/recall`, + codeToCopy: `curl -X POST ${baseUrl}/api/v1/recall \\\n -H "X-Api-Key: ${resolvedKey}" \\\n -H "Content-Type: application/json" \\\n -d '{"query": "What are the main entities?"}'`, + loading: isInitializing, + }, + { + title: "Or install the Cognee skill", + description: "Prefer skills? Run this command from your project root to create the skill file, then point your agent at it (skills directory, instructions file, or system prompt). The skill teaches your agent to call the Cognee API using the credentials from step 1.", + code: "skills/cognee/SKILL.md", + codeToCopy: GENERIC_SKILL_INSTALL, + }, + { + title: "Test the connection", + description: `Ask your agent: "What do you know from cognee?" — Cognee's memory should respond with knowledge from your brain.`, + }, + ]; + return []; + } + + function handleCardClick(key: AciAgentKey) { + trackEvent({ pageName: "Dashboard", eventName: "agent_card_clicked", additionalProperties: { card: key } }); + if (key === "upload") { onUploadClick(); return; } + setActiveKey(key); + // Preserve position if user re-opens the same agent + if (stepIndexMap[key] === undefined) { + setStepIndexMap(s => ({ ...s, [key]: 0 })); + } + } + + function goToStep(key: AciAgentKey, idx: number) { + trackEvent({ pageName: "Dashboard", eventName: "agent_step_viewed", additionalProperties: { card: key, step: String(idx) } }); + setStepIndexMap(prev => ({ ...prev, [key]: idx })); + } + + const popupOpen = activeKey !== null && activeKey !== "upload"; + const activeCfg = CARDS_CFG.find(c => c.key === activeKey); + + return ( +
+ + + {/* Agent + brain card grid — visual style from connect-agent page */} +
+ {CARDS_CFG.map((card) => { + const cardSteps = card.key !== "upload" ? getSteps(card.key as AciAgentKey) : []; + const connected = card.key === "upload" ? hasDocuments : !!integrationConnected[card.key]; + const isActive = activeKey === card.key; + const isUpload = card.key === "upload"; + + const logoNode = isUpload ? ( + // Company Brain stacked-document icon (matches main branch CompanyDataIcon) + + + + + + + + + + ) : card.key === "api-mcp" ? ( + + + + + + + ) : ( + {card.name} + ); + + const logoRight = isUpload ? -12 : card.key === "api-mcp" ? -28 : -36; + + const ctaLabel = isUpload + ? (connected ? "Add more data" : "Upload data") + : card.key === "api-mcp" ? "Connect" : "Connect agent"; + + return ( + + ); + })} +
+ + {/* Popup modal */} + {popupOpen && activeCfg && activeKey && (() => { + const steps = getSteps(activeKey); + const currentStep = stepIndexMap[activeKey] ?? 0; + return ( +
setActiveKey(null)} + > +
e.stopPropagation()} + style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", borderRadius: 14, width: 520, maxWidth: "100%", boxShadow: "0 20px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.1)", overflow: "hidden", animation: "aci-popup 200ms cubic-bezier(0.22,1,0.36,1) forwards" }} + > + {/* Modal header */} +
+ {activeKey === "api-mcp" ? ( + + ) : ( + {activeCfg?.name} + )} +
+
Connect {activeCfg.name}
+
Step {currentStep + 1} of {steps.length}
+
+ +
+ + {/* Step rows — click any row to navigate to that step */} + {steps.map((step, i) => { + const isActive = currentStep === i; + const isDone = i < currentStep; + return ( +
goToStep(activeKey, i)} + style={{ borderBottom: i < steps.length - 1 ? "1px solid rgba(255,255,255,0.07)" : "none", cursor: isActive ? "default" : "pointer" }} + > + {/* Row header — always visible */} +
+ {/* Step indicator */} +
+ {isDone ? ( + + + + ) : ( + {i + 1} + )} +
+ {/* Step title */} + + {step.title} + + {isDone && ( + + Done + + )} +
+ + {/* Expanded content — grid-template-rows animates from exact content height, + keeping modal height stable when switching between steps */} +
+
+
e.stopPropagation()} + style={{ padding: "10px 20px 18px 56px" }} + > + {step.description && ( +

{step.description}

+ )} + {step.code && ( + + )} + {step.codeBlocks && ( +
cb.label) ? 14 : 8 }}> + {step.codeBlocks.map((cb, j) => ( + cb.label ? ( +
+
{cb.label}
+ +
+ ) : ( + + ) + ))} +
+ )} + {step.skillPath && step.skillContent && ( + + )} + {i < steps.length - 1 ? ( +

+ Click step {i + 2} when ready ↓ +

+ ) : ( + + )} +
+
+
+
+ ); + })} +
+
+ ); + })()} +
+ ); +} diff --git a/cognee-frontend/src/app/(app)/dashboard/loading.tsx b/cognee-frontend/src/app/(app)/dashboard/loading.tsx new file mode 100644 index 0000000..0a02ffa --- /dev/null +++ b/cognee-frontend/src/app/(app)/dashboard/loading.tsx @@ -0,0 +1,2 @@ +import PageLoading from "@/ui/elements/PageLoading"; +export default function Loading() { return ; } diff --git a/cognee-frontend/src/app/(app)/dashboard/page.tsx b/cognee-frontend/src/app/(app)/dashboard/page.tsx new file mode 100644 index 0000000..fc554f8 --- /dev/null +++ b/cognee-frontend/src/app/(app)/dashboard/page.tsx @@ -0,0 +1,7 @@ +export const dynamic = "force-dynamic"; + +import OverviewPage from "./OverviewPage"; + +export default function Page() { + return ; +} diff --git a/cognee-frontend/src/app/(app)/datasets/DatasetsPage.tsx b/cognee-frontend/src/app/(app)/datasets/DatasetsPage.tsx new file mode 100644 index 0000000..deecd14 --- /dev/null +++ b/cognee-frontend/src/app/(app)/datasets/DatasetsPage.tsx @@ -0,0 +1,774 @@ +"use client"; + +import { captureException, recordUploadSuccess, recordUploadFailure } from "@/utils/monitoring"; +import { useState, useEffect, useRef, useCallback } from "react"; +import { useCogniInstance, useTenant } from "@/modules/tenant/TenantProvider"; +import PageLoading from "@/ui/elements/PageLoading"; +import { useFilter } from "@/ui/layout/FilterContext"; +import getDatasets from "@/modules/datasets/getDatasets"; +import getDatasetData from "@/modules/datasets/getDatasetData"; +import createDataset from "@/modules/datasets/createDataset"; +import deleteDataset from "@/modules/datasets/deleteDataset"; +import pollDatasetStatus, { type DatasetProcessingStatus } from "@/modules/datasets/pollDatasetStatus"; +import { TrackPageView, trackEvent } from "@/modules/analytics"; +import { loadGraphModelsConfig } from "@/modules/configuration/userConfiguration"; +import rememberData from "@/modules/ingestion/rememberData"; +import deleteDatasetData from "@/modules/datasets/deleteDatasetData"; +import ShareDatasetModal from "@/ui/elements/ShareDatasetModal"; +import SkeletonBar from "@/ui/elements/SkeletonBar"; + +interface DatasetRaw { + id: string; + name: string; + createdAt?: string; + ownerId?: string; +} + +interface FileEntry { + id: string; + name: string; + extension?: string; + mimeType?: string; + createdAt?: string; + size?: number; +} + +type DisplayStatus = "pending" | "running" | "completed" | "failed" | "empty" | "loading"; + +interface Dataset extends DatasetRaw { + documents: number; + status: DisplayStatus; +} + +function mapProcessingStatus(raw: DatasetProcessingStatus | undefined, docCount: number): DisplayStatus { + if (!raw) return docCount > 0 ? "completed" : "empty"; + if (raw === "DATASET_PROCESSING_COMPLETED") return "completed"; + if (raw === "DATASET_PROCESSING_ERRORED") return "failed"; + if (raw === "DATASET_PROCESSING_STARTED") return "running"; + if (raw === "DATASET_PROCESSING_INITIATED") return "pending"; + return docCount > 0 ? "completed" : "empty"; +} + +const STATUS_DOT: Record = { + pending: "#F59E0B", + running: "#F59E0B", + completed: "#22C55E", + failed: "#EF4444", + empty: "#D4D4D8", + loading: "#D4D4D8", +}; + +const EXT_META: Record = { + pdf: { fill: "#FEE2E2", stroke: "#EF4444", text: "#DC2626", label: "PDF" }, + docx: { fill: "#DBEAFE", stroke: "#3B82F6", text: "#2563EB", label: "DOC" }, + doc: { fill: "#DBEAFE", stroke: "#3B82F6", text: "#2563EB", label: "DOC" }, + md: { fill: "#F3F4F6", stroke: "#6B7280", text: "#374151", label: "MD" }, + txt: { fill: "#F3F4F6", stroke: "#9CA3AF", text: "#6B7280", label: "TXT" }, + csv: { fill: "#DCFCE7", stroke: "#22C55E", text: "#16A34A", label: "CSV" }, + json: { fill: "#FEF3C7", stroke: "#D97706", text: "#B45309", label: "JSON"}, +}; + +function decodeFilename(name: string): string { + try { + let decoded = name; + let prev: string; + do { prev = decoded; decoded = decodeURIComponent(decoded); } while (decoded !== prev); + return decoded; + } catch { + return name; + } +} + +function getExtMeta(name: string, ext?: string) { + const e = (ext || name.split(".").pop() || "").toLowerCase(); + return EXT_META[e] || { fill: "#F3F4F6", stroke: "#9CA3AF", text: "#6B7280", label: e.toUpperCase().slice(0, 4) || "FILE" }; +} + +function FileIcon({ fill, stroke, text, label }: { fill: string; stroke: string; text: string; label: string }) { + const fs = label.length > 3 ? 4.5 : label.length > 2 ? 5 : 5.5; + return ( + + + + {label} + + ); +} + +function FolderIcon() { + return ( + + + + ); +} + +function formatSize(bytes?: number): string { + if (bytes == null) return "—"; + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; +} + +function PlusIcon() { + return ; +} + +function EmptyStateIcon() { + return ( + + + + + + ); +} + +function formatDate(dateStr?: string): string { + if (!dateStr) return "—"; + return new Date(dateStr).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }); +} + +export default function DatasetsPage() { + const { cogniInstance, isInitializing } = useCogniInstance(); + const { tenant } = useTenant(); + const { datasets: contextDatasets, refreshDatasets: refreshFilterDatasets } = useFilter(); + + const [datasets, setDatasets] = useState([]); + const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); + const [outdatedDatasets, setOutdated] = useState>(new Set()); + + // Finder selection + const [selectedId, setSelectedId] = useState(null); + const [selectedDocs, setSelectedDocs] = useState([]); + const [docsLoading, setDocsLoading] = useState(false); + + // Upload + const [isDragOver, setIsDragOver] = useState(false); + const [isUploading, setIsUploading] = useState(false); + const [uploadError, setUploadError] = useState(null); + const fileInputRef = useRef(null); + const dragCounter = useRef(0); + + // Modals + const [showCreateModal, setShowCreate] = useState(false); + const [newName, setNewName] = useState(""); + const [creating, setCreating] = useState(false); + const [createError, setCreateError] = useState(""); + const [deleteTarget, setDeleteTarget] = useState(null); + const [shareTarget, setShareTarget] = useState(null); + const [deletingId, setDeletingId] = useState(null); + const [deleteDocTarget, setDeleteDocTarget] = useState(null); + const [showPasteModal, setShowPasteModal] = useState(false); + const [pasteText, setPasteText] = useState(""); + const [pasting, setPasting] = useState(false); + const inputRef = useRef(null); + const pollRef = useRef | null>(null); + + const fetchStatuses = useCallback(async (ids: string[]) => { + if (!cogniInstance || !ids.length) return; + try { + const resp = await cogniInstance.fetch("/v1/datasets/status"); + if (!resp.ok) return; + const data: Record = await resp.json(); + let completedSelectedId: string | null = null; + setDatasets((prev) => { + return prev.map((d) => { + const raw = data[d.id]; + if (!raw) return d; + const newStatus = mapProcessingStatus(raw, d.documents); + if (newStatus !== d.status) { + // When the selected brain finishes, schedule a file list refresh + if (d.id === selectedId && (d.status === "pending" || d.status === "running") && newStatus === "completed") { + completedSelectedId = d.id; + } + return { ...d, status: newStatus }; + } + return d; + }); + }); + if (completedSelectedId) { + getDatasetData(completedSelectedId, cogniInstance) + .then((docs) => { + setSelectedDocs(Array.isArray(docs) ? docs : []); + setDatasets((prev) => prev.map((d) => d.id === completedSelectedId ? { ...d, documents: Array.isArray(docs) ? docs.length : d.documents } : d)); + }) + .catch((err) => { + console.error("Failed to fetch dataset documents:", err); + setSelectedDocs([]); + }); + } + } catch { /* graceful */ } + }, [cogniInstance, selectedId]); + + useEffect(() => { + if (!cogniInstance || isInitializing) return; + loadDatasets(); + loadGraphModelsConfig(cogniInstance) + .then((cfg) => setOutdated(new Set(cfg.outdatedDatasets ?? []))) + .catch((err) => { console.error("Failed to load graph models config:", err); }); + return () => { if (pollRef.current) clearInterval(pollRef.current); }; + }, [cogniInstance, isInitializing]); + + useEffect(() => { + if (pollRef.current) clearInterval(pollRef.current); + const hasActive = datasets.some((d) => d.status === "pending" || d.status === "running"); + if (!hasActive || !cogniInstance) return; + pollRef.current = setInterval(() => fetchStatuses(datasets.map((d) => d.id)), 5000); + return () => { if (pollRef.current) clearInterval(pollRef.current); }; + }, [datasets, cogniInstance, fetchStatuses]); + + async function loadDatasets() { + if (!cogniInstance) return; + try { + let list: DatasetRaw[]; + try { + const fetched = await getDatasets(cogniInstance); + list = Array.isArray(fetched) ? fetched : []; + } catch { + list = contextDatasets as DatasetRaw[]; + } + const initial = list.map((ds) => ({ ...ds, documents: -1, status: "loading" as DisplayStatus })); + setDatasets(initial); + setLoading(false); + + const statusResp = await cogniInstance.fetch("/v1/datasets/status").catch(() => null); + const statusData: Record = statusResp?.ok ? await statusResp.json() : {}; + + for (const ds of list) { + getDatasetData(ds.id, cogniInstance) + .then((data) => { + const count = Array.isArray(data) ? data.length : 0; + setDatasets((prev) => prev.map((d) => d.id === ds.id ? { ...d, documents: count, status: mapProcessingStatus(statusData[ds.id], count) } : d)); + }) + .catch(() => { + setDatasets((prev) => prev.map((d) => d.id === ds.id ? { ...d, documents: 0, status: mapProcessingStatus(statusData[ds.id], 0) } : d)); + }); + } + } catch { + setDatasets([]); + setLoading(false); + } + } + + async function refreshSelectedDocs(id: string) { + setDocsLoading(true); + try { + const data = await getDatasetData(id, cogniInstance!); + setSelectedDocs(Array.isArray(data) ? data : []); + } catch { + setSelectedDocs([]); + } finally { + setDocsLoading(false); + } + } + + async function handleSelectDataset(id: string) { + if (selectedId === id) return; + setSelectedId(id); + setSelectedDocs([]); + await refreshSelectedDocs(id); + } + + async function handleUploadFiles(files: File[]) { + if (!cogniInstance || !selectedId || !files.length) return; + const ds = datasets.find((d) => d.id === selectedId); + if (!ds) return; + + const totalBytes = files.reduce((sum, f) => sum + f.size, 0); + const fileTypes = files.map((f) => f.type || "unknown"); + const uploadStartedAt = Date.now(); + + setIsUploading(true); + setUploadError(null); + + trackEvent({ + pageName: "Brains", + eventName: "dataset_upload_started", + additionalProperties: { + dataset_id: ds.id, + file_count: String(files.length), + total_bytes: String(totalBytes), + file_types: fileTypes.join(","), + }, + }); + + try { + // Kick off ingestion in the background so the upload POST returns immediately, + // then poll the dataset status until the pipeline finishes. This avoids holding + // one HTTP request open for the full (multi-minute) build, which the client + // would abort at the rememberData timeout — and which is also exposed to + // gateway/LB idle timeouts — while the backend kept processing. + await rememberData({ id: ds.id, name: ds.name }, files, cogniInstance, { runInBackground: true }); + await pollDatasetStatus(ds.id, cogniInstance, { intervalMs: 5000 }); + const data = await getDatasetData(ds.id, cogniInstance) as FileEntry[]; + setSelectedDocs(Array.isArray(data) ? data : []); + setDatasets((prev) => prev.map((d) => d.id === ds.id ? { ...d, documents: Array.isArray(data) ? data.length : d.documents, status: "running" } : d)); + + const durationMs = Date.now() - uploadStartedAt; + recordUploadSuccess(durationMs, totalBytes, files.length); + trackEvent({ + pageName: "Brains", + eventName: "dataset_files_uploaded", + additionalProperties: { + dataset_id: ds.id, + file_count: String(files.length), + total_bytes: String(totalBytes), + duration_ms: String(durationMs), + }, + }); + } catch (err) { + const durationMs = Date.now() - uploadStartedAt; + const errorName = err instanceof Error ? err.name : "UnknownError"; + const errorMessage = err instanceof Error ? err.message : String(err); + + recordUploadFailure(errorName, durationMs); + trackEvent({ + pageName: "Brains", + eventName: "dataset_upload_failed", + additionalProperties: { + dataset_id: ds.id, + file_count: String(files.length), + total_bytes: String(totalBytes), + file_types: fileTypes.join(","), + duration_ms: String(durationMs), + error_name: errorName, + error_message: errorMessage, + }, + }); + + if (errorName === "UploadTimeoutError") { + setUploadError("The file took too long to process. Please try again with a smaller file."); + } else { + captureException(err, { datasetId: ds.id, fileCount: files.length, totalBytes, durationMs }); + setUploadError(errorMessage || "Upload failed. Please try again."); + } + } finally { + setIsUploading(false); + } + } + + async function handleDeleteFile(docId: string) { + if (!cogniInstance || !selectedId) return; + try { + await deleteDatasetData(selectedId, docId, cogniInstance); + setSelectedDocs((prev) => prev.filter((d) => d.id !== docId)); + setDatasets((prev) => prev.map((d) => d.id === selectedId ? { ...d, documents: Math.max(0, d.documents - 1) } : d)); + } catch (err) { + console.error("Failed to delete file:", err); + } + } + + const handleDelete = async (ds: Dataset) => { + if (!cogniInstance) return; + setDeletingId(ds.id); + try { + await deleteDataset(ds.id, cogniInstance); + trackEvent({ pageName: "Brains", eventName: "dataset_deleted", additionalProperties: { dataset_id: ds.id, dataset_name: ds.name } }); + setDatasets((prev) => prev.filter((d) => d.id !== ds.id)); + if (selectedId === ds.id) { setSelectedId(null); setSelectedDocs([]); } + setDeleteTarget(null); + refreshFilterDatasets(); + } catch (err) { + console.error("Failed to delete brain:", err); + } finally { + setDeletingId(null); + } + }; + + const handleCreate = async () => { + const trimmed = newName.trim(); + if (!trimmed || !cogniInstance) return; + setCreateError(""); + if (trimmed.includes(" ") || trimmed.includes(".")) { + setCreateError("Dataset name cannot contain spaces or periods."); + return; + } + setCreating(true); + try { + const ds = await createDataset({ name: trimmed.toLowerCase() }, cogniInstance, tenant?.tenant_id); + trackEvent({ pageName: "Brains", eventName: "dataset_created", additionalProperties: { dataset_name: ds.name } }); + setDatasets((prev) => [...prev, { ...ds, documents: 0, status: "empty" as DisplayStatus }]); + setNewName(""); setCreateError(""); setShowCreate(false); + refreshFilterDatasets(); + } catch (err) { + console.error("Failed to create dataset:", err); + setCreateError("Failed to create brain. Please try again."); + } finally { + setCreating(false); + } + }; + + async function handlePasteText() { + if (!pasteText.trim() || !selectedId) return; + setPasting(true); + try { + const blob = new Blob([pasteText], { type: "text/plain" }); + const file = new File([blob], `pasted-text-${Date.now()}.txt`, { type: "text/plain" }); + setShowPasteModal(false); + setPasteText(""); + await handleUploadFiles([file]); + } finally { + setPasting(false); + } + } + + if (loading || isInitializing) { + return ( + <> +
+ Loading datasets… +
+ + ); + } + + const selectedDataset = datasets.find((d) => d.id === selectedId) ?? null; + + return ( +
+ + + {/* ── Create modal ── */} + {showCreateModal && ( +
{ setShowCreate(false); setCreateError(""); }}> +
e.stopPropagation()} style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 12, padding: 24, width: 420, display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 16px 48px rgba(0,0,0,0.12)" }}> +

Create brain

+

Give your brain a name. You can upload documents after creation.

+ setNewName(e.target.value)} + onKeyDown={(e) => { if (e.key === "Enter") handleCreate(); }} + placeholder="e.g. product-docs, sec-filings..." + style={{ width: "100%", height: 40, background: "rgba(255,255,255,0.06)", border: "1px solid rgba(255,255,255,0.12)", borderRadius: 8, paddingInline: 14, fontSize: 14, color: "#EDECEA", fontFamily: "inherit", outline: "none", boxSizing: "border-box" }} + onFocus={(e) => { e.target.style.borderColor = "#6510F4"; e.target.style.boxShadow = "0 0 0 3px rgba(188,155,255,0.10)"; }} + onBlur={(e) => { e.target.style.borderColor = "rgba(255,255,255,0.12)"; e.target.style.boxShadow = "none"; }} + /> + {createError &&

{createError}

} +
+ + +
+
+
+ )} + + {/* ── Delete document modal ── */} + {deleteDocTarget && ( +
setDeleteDocTarget(null)}> +
e.stopPropagation()} style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 12, padding: 24, width: 420, display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 16px 48px rgba(0,0,0,0.12)" }}> +

Delete document

+

+ Are you sure you want to delete {decodeFilename(deleteDocTarget.name)}? This action cannot be undone. +

+
+ + +
+
+
+ )} + + {/* ── Paste text modal ── */} + {showPasteModal && ( +
{ setShowPasteModal(false); setPasteText(""); }}> +
e.stopPropagation()} style={{ background: "rgba(15,15,15,0.92)", backdropFilter: "blur(16px)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 12, padding: 24, width: 420, display: "flex", flexDirection: "column", gap: 16, boxShadow: "0 16px 48px rgba(0,0,0,0.12)" }}> +

Paste text

+

Paste your text below. It will be added as a document to the selected brain.

+