diff --git a/CHANGELOG.md b/CHANGELOG.md index bcde999..3230e65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.8.2] - 2026-04-06 + +### Added +- DashScope (Qwen) provider support — Alibaba Cloud's Qwen models (qwen-turbo, qwen-plus, qwen-max) +- MiniMax provider support — MiniMax models with 204K context (MiniMax-M2.5, MiniMax-M2.5-highspeed) +- Model discovery, connection testing, and credential management for both new providers +- Documentation for DashScope and MiniMax in AI providers guide, environment reference, and provider comparison + +### Dependencies +- Bump esperanto to >= 2.20.0 + ## [1.8.1] - 2026-03-10 ### Added diff --git a/README.md b/README.md index 370f932..d3c4621 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ In a world dominated by Artificial Intelligence, having the ability to think **Open Notebook empowers you to:** - 🔒 **Control your data** - Keep your research private and secure -- 🤖 **Choose your AI models** - Support for 16+ providers including OpenAI, Anthropic, Ollama, LM Studio, and more +- 🤖 **Choose your AI models** - Support for 18+ providers including OpenAI, Anthropic, Ollama, LM Studio, and more - 📚 **Organize multi-modal content** - PDFs, videos, audio, web pages, and more - 🎙️ **Generate professional podcasts** - Advanced multi-speaker podcast generation - 🔍 **Search intelligently** - Full-text and vector search across all your content @@ -74,7 +74,7 @@ Learn more about our project at [https://www.open-notebook.ai](https://www.open- | Feature | Open Notebook | Google Notebook LM | Advantage | |---------|---------------|--------------------|-----------| | **Privacy & Control** | Self-hosted, your data | Google cloud only | Complete data sovereignty | -| **AI Provider Choice** | 16+ providers (OpenAI, Anthropic, Ollama, LM Studio, etc.) | Google models only | Flexibility and cost optimization | +| **AI Provider Choice** | 18+ providers (OpenAI, Anthropic, Ollama, LM Studio, etc.) | Google models only | Flexibility and cost optimization | | **Podcast Speakers** | 1-4 speakers with custom profiles | 2 speakers only | Extreme flexibility | | **Content Transformations** | Custom and built-in | Limited options | Unlimited processing power | | **API Access** | Full REST API | No API | Complete automation | @@ -213,6 +213,8 @@ Thanks to the [Esperanto](https://github.com/lfnovo/esperanto) library, we suppo | Voyage | ❌ | ✅ | ❌ | ❌ | | xAI | ✅ | ❌ | ❌ | ❌ | | OpenRouter | ✅ | ❌ | ❌ | ❌ | +| DashScope (Qwen) | ✅ | ❌ | ❌ | ❌ | +| MiniMax | ✅ | ❌ | ❌ | ❌ | | OpenAI Compatible* | ✅ | ❌ | ❌ | ❌ | *Supports LM Studio and any OpenAI-compatible endpoint @@ -223,7 +225,7 @@ Thanks to the [Esperanto](https://github.com/lfnovo/esperanto) library, we suppo - **🔒 Privacy-First**: Your data stays under your control - no cloud dependencies - **🎯 Multi-Notebook Organization**: Manage multiple research projects seamlessly - **📚 Universal Content Support**: PDFs, videos, audio, web pages, Office docs, and more -- **🤖 Multi-Model AI Support**: 16+ providers including OpenAI, Anthropic, Ollama, Google, LM Studio, and more +- **🤖 Multi-Model AI Support**: 18+ providers including OpenAI, Anthropic, Ollama, Google, LM Studio, and more - **🎙️ Professional Podcast Generation**: Advanced multi-speaker podcasts with Episode Profiles - **🔍 Intelligent Search**: Full-text and vector search across all your content - **💬 Context-Aware Chat**: AI conversations powered by your research materials @@ -280,7 +282,7 @@ Thanks to the [Esperanto](https://github.com/lfnovo/esperanto) library, we suppo ### Recently Completed ✅ - **Next.js Frontend**: Modern React-based frontend with improved performance - **Comprehensive REST API**: Full programmatic access to all functionality -- **Multi-Model Support**: 16+ AI providers including OpenAI, Anthropic, Ollama, LM Studio +- **Multi-Model Support**: 18+ AI providers including OpenAI, Anthropic, Ollama, LM Studio - **Advanced Podcast Generator**: Professional multi-speaker podcasts with Episode Profiles - **Content Transformations**: Powerful customizable actions for content processing - **Enhanced Citations**: Improved layout and finer control for source citations diff --git a/api/credentials_service.py b/api/credentials_service.py index 943464c..9ad77f3 100644 --- a/api/credentials_service.py +++ b/api/credentials_service.py @@ -57,6 +57,8 @@ PROVIDER_ENV_CONFIG: Dict[str, dict] = { "openai_compatible": { "required_any": ["OPENAI_COMPATIBLE_BASE_URL", "OPENAI_COMPATIBLE_API_KEY"], }, + "dashscope": {"required": ["DASHSCOPE_API_KEY"]}, + "minimax": {"required": ["MINIMAX_API_KEY"]}, } PROVIDER_MODALITIES: Dict[str, List[str]] = { @@ -74,6 +76,8 @@ PROVIDER_MODALITIES: Dict[str, List[str]] = { "vertex": ["language", "embedding"], "azure": ["language", "embedding", "speech_to_text", "text_to_speech"], "openai_compatible": ["language", "embedding", "speech_to_text", "text_to_speech"], + "dashscope": ["language"], + "minimax": ["language"], } @@ -512,6 +516,8 @@ async def discover_with_config(provider: str, config: dict) -> List[dict]: "deepseek": "https://api.deepseek.com/models", "xai": "https://api.x.ai/v1/models", "openrouter": "https://openrouter.ai/api/v1/models", + "dashscope": "https://dashscope.aliyuncs.com/compatible-mode/v1/models", + "minimax": "https://api.minimax.io/v1/models", } if provider == "ollama": diff --git a/api/routers/models.py b/api/routers/models.py index 7a2ef9b..df28463 100644 --- a/api/routers/models.py +++ b/api/routers/models.py @@ -96,6 +96,8 @@ PROVIDER_PRIORITY = [ "ollama", "azure", "openai_compatible", + "dashscope", + "minimax", ] # Model preference patterns (preferred models within each provider) @@ -105,6 +107,8 @@ MODEL_PREFERENCES = { "google": ["gemini-2.0", "gemini-1.5-pro", "gemini-pro"], "mistral": ["mistral-large", "mixtral"], "groq": ["llama-3.3", "llama-3.1", "mixtral"], + "dashscope": ["qwen-max", "qwen-plus", "qwen-turbo"], + "minimax": ["MiniMax-M2.5", "MiniMax-M2.5-highspeed"], } @@ -378,6 +382,8 @@ async def get_provider_availability(): "voyage": "VOYAGE_API_KEY", "elevenlabs": "ELEVENLABS_API_KEY", "ollama": "OLLAMA_API_BASE", + "dashscope": "DASHSCOPE_API_KEY", + "minimax": "MINIMAX_API_KEY", } provider_status = {} diff --git a/docs/0-START-HERE/index.md b/docs/0-START-HERE/index.md index 5f44f75..3d7c985 100644 --- a/docs/0-START-HERE/index.md +++ b/docs/0-START-HERE/index.md @@ -12,7 +12,7 @@ --- ### ☁️ I want to use other cloud AI (Anthropic, Google, OpenRouter, etc.) -**5 minutes to running.** Choose from 15+ AI providers. +**5 minutes to running.** Choose from 17+ AI providers. → [Cloud Providers Quick Start](quick-start-cloud.md) @@ -39,7 +39,7 @@ | Feature | Open Notebook | Notebook LM | |---------|---|---| | **Privacy** | Self-hosted, your control | Cloud, Google's servers | -| **AI Choice** | 15+ providers | Google's models only | +| **AI Choice** | 17+ providers | Google's models only | | **Podcast Speakers** | 1-4 customizable | 2 only | | **Cost** | Completely free | Free (but your data) | | **Offline** | Yes | No | diff --git a/docs/4-AI-PROVIDERS/index.md b/docs/4-AI-PROVIDERS/index.md index be478da..2a5786b 100644 --- a/docs/4-AI-PROVIDERS/index.md +++ b/docs/4-AI-PROVIDERS/index.md @@ -1,6 +1,6 @@ # AI Providers - Comparison & Selection Guide -Open Notebook supports 15+ AI providers. This guide helps you **choose the right provider** for your needs. +Open Notebook supports 17+ AI providers. This guide helps you **choose the right provider** for your needs. > 💡 **Just want to set up a provider?** Skip to the [Configuration Guide](../5-CONFIGURATION/ai-providers.md) for detailed setup instructions. @@ -54,6 +54,24 @@ Open Notebook supports 15+ AI providers. This guide helps you **choose the right → [Setup Guide](../5-CONFIGURATION/ai-providers.md#openrouter) +**DashScope (Qwen)** +- Cost: ~$0.01-0.06 per 1K tokens +- Speed: Fast +- Quality: Good +- Best for: Users in Asia, Alibaba Cloud ecosystem +- Advantage: Competitive pricing, strong multilingual support + +→ [Setup Guide](../5-CONFIGURATION/ai-providers.md#dashscope-qwen) + +**MiniMax** +- Cost: Varies by model +- Speed: Fast +- Quality: Good +- Best for: Long context tasks (204K tokens) +- Advantage: Very long context window + +→ [Setup Guide](../5-CONFIGURATION/ai-providers.md#minimax) + ### Local / Self-Hosted (Free) **Ollama (Recommended for Local)** @@ -98,6 +116,8 @@ Open Notebook supports 15+ AI providers. This guide helps you **choose the right | **Google** | Very Fast | $$ | Good-Excellent | Low | 5 min | 2M | | **Groq** | Ultra Fast | $ | Good | Low | 5 min | 32K | | **OpenRouter** | Varies | Varies | Varies | Low | 5 min | Varies | +| **DashScope** | Fast | $ | Good | Low | 5 min | Varies | +| **MiniMax** | Fast | $$ | Good | Low | 5 min | 204K | | **Ollama** | Slow-Medium | Free | Good | Max | 10 min | Varies | | **LM Studio** | Slow-Medium | Free | Good | Max | 15 min | Varies | | **Azure** | Very Fast | $$ | Excellent | High | 10 min | 128K | diff --git a/docs/5-CONFIGURATION/ai-providers.md b/docs/5-CONFIGURATION/ai-providers.md index b6a4134..dc2f590 100644 --- a/docs/5-CONFIGURATION/ai-providers.md +++ b/docs/5-CONFIGURATION/ai-providers.md @@ -249,6 +249,76 @@ Heavy use: Depends on models chosen --- +### DashScope (Qwen) + +**Cost:** ~$0.01-0.06 per 1K tokens (varies by model) + +**Get Your API Key:** +1. Go to https://dashscope.console.aliyun.com/ +2. Create an Alibaba Cloud account (if needed) +3. Navigate to API Keys section +4. Create a new API key + +**Configure in Open Notebook:** +1. Go to **Settings** → **API Keys** +2. Click **Add Credential** +3. Select provider: **DashScope (Qwen)** +4. Give it a name, paste your API key +5. Click **Save**, then **Test Connection** +6. Click **Discover Models** → **Register Models** + +**Available Models:** +- `qwen-max` — Most capable Qwen model +- `qwen-plus` — Good balance of quality and speed +- `qwen-turbo` — Fastest, cheapest + +**Recommended:** +- For quality: `qwen-max` (best overall) +- For general use: `qwen-plus` (good balance) +- For speed/cost: `qwen-turbo` (cheapest) + +**Troubleshooting:** +- "Invalid API key" → Check the key in the DashScope console +- "Model not available" → Re-discover models from the credential + +--- + +### MiniMax + +**Cost:** Varies by model + +**Get Your API Key:** +1. Go to https://platform.minimaxi.com/ +2. Create an account (if needed) +3. Navigate to API Keys section +4. Create a new API key + +**Configure in Open Notebook:** +1. Go to **Settings** → **API Keys** +2. Click **Add Credential** +3. Select provider: **MiniMax** +4. Give it a name, paste your API key +5. Click **Save**, then **Test Connection** +6. Click **Discover Models** → **Register Models** + +**Available Models:** +- `MiniMax-M2.5` — Most capable, 204K context +- `MiniMax-M2.5-highspeed` — Faster variant, 204K context + +**Recommended:** +- For quality: `MiniMax-M2.5` (best overall) +- For speed: `MiniMax-M2.5-highspeed` (faster responses) + +**Advantages:** +- Very long context (204K tokens) +- Competitive pricing + +**Troubleshooting:** +- "Invalid API key" → Check the key in the MiniMax platform +- "Model not available" → Re-discover models from the credential + +--- + ## Self-Hosted / Local ### Ollama (Recommended for Local) diff --git a/docs/5-CONFIGURATION/environment-reference.md b/docs/5-CONFIGURATION/environment-reference.md index decb10d..ca50944 100644 --- a/docs/5-CONFIGURATION/environment-reference.md +++ b/docs/5-CONFIGURATION/environment-reference.md @@ -263,6 +263,8 @@ If you have these variables configured from a previous installation, click the * | `OPENAI_COMPATIBLE_API_KEY_STT` | OpenAI-Compatible | Configure per-service key in credential | | `OPENAI_COMPATIBLE_BASE_URL_TTS` | OpenAI-Compatible | Configure per-service URL in credential | | `OPENAI_COMPATIBLE_API_KEY_TTS` | OpenAI-Compatible | Configure per-service key in credential | +| `DASHSCOPE_API_KEY` | DashScope (Qwen) | Settings → API Keys → Add DashScope Credential | +| `MINIMAX_API_KEY` | MiniMax | Settings → API Keys → Add MiniMax Credential | | `AZURE_OPENAI_API_KEY` | Azure OpenAI | Settings → API Keys → Add Azure OpenAI Credential | | `AZURE_OPENAI_ENDPOINT` | Azure OpenAI | Configure in Azure OpenAI credential | | `AZURE_OPENAI_API_VERSION` | Azure OpenAI | Configure in Azure OpenAI credential | diff --git a/docs/6-TROUBLESHOOTING/faq.md b/docs/6-TROUBLESHOOTING/faq.md index ff798cc..ded082a 100644 --- a/docs/6-TROUBLESHOOTING/faq.md +++ b/docs/6-TROUBLESHOOTING/faq.md @@ -18,7 +18,7 @@ Open Notebook is an open-source, privacy-focused alternative to Google's Noteboo ### How is it different from Google Notebook LM? **Privacy**: Your data stays local by default. Only your chosen AI providers receive queries. -**Flexibility**: Support for 15+ AI providers (OpenAI, Anthropic, Google, local models, etc.) +**Flexibility**: Support for 17+ AI providers (OpenAI, Anthropic, Google, local models, etc.) **Customization**: Open source, so you can modify and extend functionality **Control**: You control your data, models, and processing diff --git a/docs/7-DEVELOPMENT/design-principles.md b/docs/7-DEVELOPMENT/design-principles.md index 7ca5346..d243a6f 100644 --- a/docs/7-DEVELOPMENT/design-principles.md +++ b/docs/7-DEVELOPMENT/design-principles.md @@ -86,7 +86,7 @@ Open Notebook aims to be a **privacy-focused, self-hosted alternative to Google' - Graceful degradation when providers are unavailable **Example Decisions**: -- ✅ Support for 16+ AI providers +- ✅ Support for 18+ AI providers - ✅ Per-feature model selection (chat, embeddings, TTS) - ❌ Features that only work with OpenAI - ❌ Hard-coded API endpoints for specific providers diff --git a/frontend/src/app/(dashboard)/settings/api-keys/page.tsx b/frontend/src/app/(dashboard)/settings/api-keys/page.tsx index 95b16a0..27bde0e 100644 --- a/frontend/src/app/(dashboard)/settings/api-keys/page.tsx +++ b/frontend/src/app/(dashboard)/settings/api-keys/page.tsx @@ -75,12 +75,14 @@ const PROVIDER_DISPLAY_NAMES: Record = { azure: 'Azure OpenAI', vertex: 'Google Vertex AI', openai_compatible: 'OpenAI Compatible', + dashscope: 'DashScope (Qwen)', + minimax: 'MiniMax', } // All providers in display order const ALL_PROVIDERS = [ 'openai', 'anthropic', 'google', 'groq', 'mistral', 'deepseek', - 'xai', 'openrouter', 'voyage', 'elevenlabs', 'ollama', + 'xai', 'openrouter', 'dashscope', 'minimax', 'voyage', 'elevenlabs', 'ollama', 'azure', 'vertex', 'openai_compatible', ] @@ -100,6 +102,8 @@ const PROVIDER_MODALITIES: Record = { azure: ['language', 'embedding', 'text_to_speech', 'speech_to_text'], vertex: ['language', 'embedding', 'text_to_speech'], openai_compatible: ['language', 'embedding', 'text_to_speech', 'speech_to_text'], + dashscope: ['language'], + minimax: ['language'], } // Documentation links @@ -117,6 +121,8 @@ const PROVIDER_DOCS: Record = { azure: 'https://portal.azure.com/#view/Microsoft_Azure_ProjectOxford/CognitiveServicesHub/~/OpenAI', vertex: 'https://cloud.google.com/vertex-ai/docs/start/cloud-environment', openai_compatible: 'https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/openai-compatible.md', + dashscope: 'https://help.aliyun.com/zh/model-studio/getting-started/', + minimax: 'https://platform.minimaxi.com/document/Guides', } const TYPE_ICONS: Record = { diff --git a/open_notebook/ai/connection_tester.py b/open_notebook/ai/connection_tester.py index 1e6c7c9..99e52d4 100644 --- a/open_notebook/ai/connection_tester.py +++ b/open_notebook/ai/connection_tester.py @@ -34,6 +34,8 @@ TEST_MODELS = { "vertex": ("gemini-2.0-flash", "language"), # Uses Google Vertex AI "azure": ("gpt-35-turbo", "language"), # Azure OpenAI deployment name "openai_compatible": (None, "language"), # Dynamic - will use first available model + "dashscope": ("qwen-plus", "language"), + "minimax": ("MiniMax-M2.5", "language"), } diff --git a/open_notebook/ai/key_provider.py b/open_notebook/ai/key_provider.py index fb4e497..9a54d9e 100644 --- a/open_notebook/ai/key_provider.py +++ b/open_notebook/ai/key_provider.py @@ -62,6 +62,12 @@ PROVIDER_CONFIG = { "ollama": { "env_var": "OLLAMA_API_BASE", }, + "dashscope": { + "env_var": "DASHSCOPE_API_KEY", + }, + "minimax": { + "env_var": "MINIMAX_API_KEY", + }, } diff --git a/open_notebook/ai/model_discovery.py b/open_notebook/ai/model_discovery.py index 726d70c..7270843 100644 --- a/open_notebook/ai/model_discovery.py +++ b/open_notebook/ai/model_discovery.py @@ -131,6 +131,14 @@ ELEVENLABS_MODEL_TYPES = { "text_to_speech": ["eleven"], } +DASHSCOPE_MODEL_TYPES = { + "language": ["qwen"], +} + +MINIMAX_MODEL_TYPES = { + "language": ["minimax", "abab"], +} + def classify_model_type(model_name: str, provider: str) -> str: """ @@ -150,6 +158,8 @@ def classify_model_type(model_name: str, provider: str) -> str: "xai": XAI_MODEL_TYPES, "voyage": VOYAGE_MODEL_TYPES, "elevenlabs": ELEVENLABS_MODEL_TYPES, + "dashscope": DASHSCOPE_MODEL_TYPES, + "minimax": MINIMAX_MODEL_TYPES, } mapping = type_mappings.get(provider, {}) @@ -518,6 +528,74 @@ async def discover_elevenlabs_models() -> List[DiscoveredModel]: ] +async def discover_dashscope_models() -> List[DiscoveredModel]: + """Fetch available models from DashScope (Qwen) API.""" + api_key = os.environ.get("DASHSCOPE_API_KEY") + if not api_key: + return [] + + models = [] + try: + async with httpx.AsyncClient() as client: + response = await client.get( + "https://dashscope.aliyuncs.com/compatible-mode/v1/models", + headers={"Authorization": f"Bearer {api_key}"}, + timeout=30.0, + ) + response.raise_for_status() + data = response.json() + + for model in data.get("data", []): + model_id = model.get("id", "") + if model_id: + model_type = classify_model_type(model_id, "dashscope") + models.append( + DiscoveredModel( + name=model_id, + provider="dashscope", + model_type=model_type, + ) + ) + except Exception as e: + logger.warning(f"Failed to discover DashScope models: {e}") + + return models + + +async def discover_minimax_models() -> List[DiscoveredModel]: + """Fetch available models from MiniMax API.""" + api_key = os.environ.get("MINIMAX_API_KEY") + if not api_key: + return [] + + models = [] + try: + async with httpx.AsyncClient() as client: + response = await client.get( + "https://api.minimax.io/v1/models", + headers={"Authorization": f"Bearer {api_key}"}, + timeout=30.0, + ) + response.raise_for_status() + data = response.json() + + for model in data.get("data", []): + model_id = model.get("id", "") + if model_id: + model_type = classify_model_type(model_id, "minimax") + models.append( + DiscoveredModel( + name=model_id, + provider="minimax", + model_type=model_type, + ) + ) + except Exception as e: + logger.warning(f"Failed to discover MiniMax models: {e}") + + return models + + async def discover_openai_compatible_models() -> List[DiscoveredModel]: """ Fetch available models from an OpenAI-compatible API endpoint. @@ -600,6 +678,8 @@ PROVIDER_DISCOVERY_FUNCTIONS = { "voyage": discover_voyage_models, "elevenlabs": discover_elevenlabs_models, "openai_compatible": discover_openai_compatible_models, + "dashscope": discover_dashscope_models, + "minimax": discover_minimax_models, "azure": None, # Azure requires credential-based discovery (different auth) "vertex": None, # Vertex requires credential-based discovery (service account) } diff --git a/pyproject.toml b/pyproject.toml index 8b1e549..b45be49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "open-notebook" -version = "1.8.1" +version = "1.8.2" description = "An open source implementation of a research assistant, inspired by Google Notebook LM" authors = [ {name = "Luis Novo", email = "lfnovo@gmail.com"} @@ -34,7 +34,7 @@ dependencies = [ "httpx[socks]>=0.27.0", "content-core>=1.14.1,<2", "ai-prompter>=0.3,<1", - "esperanto>=2.19.7,<3", + "esperanto>=2.20.0,<3", "surrealdb>=1.0.4", "podcast-creator>=0.12.0,<1", "surreal-commands>=1.3.1,<2", diff --git a/uv.lock b/uv.lock index d41dea4..0b7ef74 100644 --- a/uv.lock +++ b/uv.lock @@ -648,15 +648,15 @@ wheels = [ [[package]] name = "esperanto" -version = "2.19.7" +version = "2.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/e0/d7c7f91a7b86744ce040385a4c81f8eaa147a140f299995c1fb8a59c4238/esperanto-2.19.7.tar.gz", hash = "sha256:83b2b1683361fc019444305a34ae398da71724f76b9563c0cf5a7c4d54fb08eb", size = 833887, upload-time = "2026-03-11T20:25:41.47Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/8c/eba93a2b7b1a4fc6812bcf0a83a69a3376fcaf5831fae04a0ad539baeff6/esperanto-2.20.0.tar.gz", hash = "sha256:53a16c539ed2f83e3400b4e56d5a20b7873df6cb926c1e6696a9611b3be928fb", size = 845084, upload-time = "2026-03-21T14:32:04.589Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/ab/6d81682bfd3b2f5db283c302c4695623f282ae08c99c667609467c10bc22/esperanto-2.19.7-py3-none-any.whl", hash = "sha256:9a853a51f59495ac2487c3d7a12942c931e4daf6185e45247b715938a4597685", size = 202436, upload-time = "2026-03-11T20:25:40.089Z" }, + { url = "https://files.pythonhosted.org/packages/13/83/476dd4d7ee9f1b013ccd66e2518d150e66d3537736aed735b27230af0eb4/esperanto-2.20.0-py3-none-any.whl", hash = "sha256:767f95d08dd29e7d1950f4cc2ec908084e93ec61e945ce9446f001c54b86bb63", size = 203977, upload-time = "2026-03-21T14:32:05.676Z" }, ] [[package]] @@ -2129,7 +2129,7 @@ requires-dist = [ { name = "ai-prompter", specifier = ">=0.3,<1" }, { name = "babel", specifier = ">=2.18.0" }, { name = "content-core", specifier = ">=1.14.1,<2" }, - { name = "esperanto", specifier = ">=2.19.7,<3" }, + { name = "esperanto", specifier = ">=2.20.0,<3" }, { name = "fastapi", specifier = ">=0.104.0" }, { name = "httpx", extras = ["socks"], specifier = ">=0.27.0" }, { name = "ipykernel", marker = "extra == 'dev'", specifier = ">=6.29.5" },