eq-chatbot-core 3.2.0__tar.gz → 3.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eq_chatbot_core-3.3.0/AGENTS.md +225 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/CHANGELOG.md +137 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/CLAUDE.md +2 -1
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/PKG-INFO +6 -5
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/README.md +4 -4
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/RELEASE_NOTES.md +56 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/mcp.md +100 -16
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/pyproject.toml +7 -0
- eq_chatbot_core-3.3.0/src/eq_chatbot_core/mcp/__init__.py +123 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/mcp/client.py +13 -5
- eq_chatbot_core-3.3.0/src/eq_chatbot_core/mcp/streamable_http.py +415 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/version.py +1 -1
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/reports/latest.md +108 -53
- eq_chatbot_core-3.3.0/tests/unit/test_docs_extra_pyproject.py +61 -0
- eq_chatbot_core-3.3.0/tests/unit/test_mcp_streamable_http.py +439 -0
- eq_chatbot_core-3.2.0/src/eq_chatbot_core/mcp/__init__.py +0 -90
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.claude/settings.local.json +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.github/workflows/ci.yml +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.gitignore +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/MILESTONES.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/PROJECT.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/REQUIREMENTS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/ROADMAP.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/STATE.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/codebase/ARCHITECTURE.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/codebase/CONCERNS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/codebase/CONVENTIONS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/codebase/INTEGRATIONS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/codebase/STACK.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/codebase/STRUCTURE.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/codebase/TESTING.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/config.json +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/00-codebase-cleanup/00-01-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/00-codebase-cleanup/00-01-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/00-codebase-cleanup/00-02-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/00-codebase-cleanup/00-02-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/00-codebase-cleanup/00-03-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/00-codebase-cleanup/00-03-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/00-codebase-cleanup/00-VERIFICATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-01-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-01-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-02-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-02-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-03-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-03-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-04-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-04-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-05-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-05-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-PATTERNS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-RESEARCH.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-REVIEW-FIX.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-REVIEW.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-VALIDATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/01-contracts-foundation/01-VERIFICATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-01-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-01-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-02-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-02-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-03-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-03-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-CONTEXT.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-DISCUSSION-LOG.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-PATTERNS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-RESEARCH.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-REVIEW.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-VALIDATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/02-openai-realtime-provider/02-VERIFICATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-01-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-01-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-02-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-02-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-03-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-03-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-04-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-04-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-05-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-05-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-CONTEXT.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-DISCUSSION-LOG.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-HUMAN-UAT.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-PATTERNS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-RESEARCH.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-VALIDATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03-gemini-live-nova-sonic-stub/03-VERIFICATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/.gitkeep +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-01-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-01-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-02-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-02-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-03-PLAN.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-03-SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-CONTEXT.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-HUMAN-UAT.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-PATTERNS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-RESEARCH.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-VALIDATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/phases/03.1-elevenlabs-agents-realtime-provider/03.1-VERIFICATION.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/research/ARCHITECTURE.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/research/FEATURES.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/research/PITFALLS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/research/STACK.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.planning/research/SUMMARY.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.pre-commit-config.yaml +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/.project-tips +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/LICENSE +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/MANIFEST.in +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/TASKS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/README.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/cli.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/providers.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/rag.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/reasoning.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/security.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/server-mode.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/superpowers/specs/2026-05-25-elevenlabs-realtime-provider-design.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/docs/testing.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/cli.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/data/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/data/capability_catalog.json +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/data/capability_overrides.json +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/data/config.toml.example +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/anthropic_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/base.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/ionos_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/langdock_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/litellm_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/local_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/mammouth_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/melious_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/openai_compatible.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/openai_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/openrouter_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/privatemode_provider.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/stream_accumulator.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/providers/temperature_constraints.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/py.typed +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/rag/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/rag/chunker.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/rag/context_manager.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/rag/embedder.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/rag/retriever.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/abc.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/contracts.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/factory.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/mock.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/providers/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/providers/elevenlabs.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/providers/gemini_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/providers/openai.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/realtime/websocket_client.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/security/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/security/encryption.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/security/file_validator.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/security/injection.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/security/rate_limit.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/server/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/server/app.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/server/auth.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/server/lifecycle.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/server/models.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/server/streaming.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/services/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/services/capability_catalog.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/services/document_extractor.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/services/error_handler.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/services/knowledge_service.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/utils/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/utils/config.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/utils/image.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/utils/pdf.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/utils/secret_scrub.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/src/eq_chatbot_core/utils/url_validation.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/.env.example +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/INTEGRATION_TESTS.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/conftest.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_ionos_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_litellm_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_local_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_mammouth_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_mcp_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_melious_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_openai_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_openrouter_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_privatemode_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_realtime_elevenlabs.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_realtime_gemini_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/integration/test_realtime_openai_live.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/model_registry.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/conftest.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_contracts.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_factory.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_import_guard.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_mock.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_pyproject.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_realtime_elevenlabs.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_realtime_gemini.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_realtime_openai.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/realtime/test_websocket_client.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/server/__init__.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/server/test_app.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/server/test_auth.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/server/test_cli_serve.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/server/test_lifecycle.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/server/test_streaming.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_anthropic.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_anthropic_temperature_transport.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_capability_catalog.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_chunker.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_cli_api_key.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_cli_chat.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_cli_image.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_cli_listing_assets.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_config.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_context_manager.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_document_extractor.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_embedder.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_encryption.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_error_handler.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_exceptions.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_factory.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_file_validator.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_gpt5_temperature.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_injection.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_ionos.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_knowledge_service.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_langdock.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_langdock_agent_errors.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_langdock_agent_streaming.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_langdock_backends.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_langdock_google_models.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_langdock_http_error_detail.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_list_models_schema.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_litellm.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_local.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_mammouth.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_mcp.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_melious.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_openai.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_openai_image.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_openrouter.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_openrouter_error_handling.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_openrouter_image.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_pdf.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_privatemode.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_rate_limit.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_retriever.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_security_hardening.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_stream_accumulator.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_temperature_constraints.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_tool_normalization.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_url_validation.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_utils_image.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/tests/unit/test_websocket_is_connected.py +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/usage/AGENT.md +0 -0
- {eq_chatbot_core-3.2.0 → eq_chatbot_core-3.3.0}/uv.lock +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to AI coding agents when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Standalone Python library for LLM chatbot integration. Provides unified API across multiple providers (OpenAI, Anthropic, LangDock, OpenRouter, local servers). Originally extracted from v18-chatbot Odoo project for independent PyPI publishing.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
### Development Setup
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv venv && source .venv/bin/activate # or: venv+
|
|
15
|
+
uv pip install -e ".[dev,pdf,security]"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Testing
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Run all unit tests (fast, mocked)
|
|
22
|
+
pytest tests/unit/ -v
|
|
23
|
+
|
|
24
|
+
# Run all tests with coverage
|
|
25
|
+
pytest tests/ -v --cov=eq_chatbot_core --cov-report=html
|
|
26
|
+
|
|
27
|
+
# Run single test file
|
|
28
|
+
pytest tests/unit/test_openai.py -v
|
|
29
|
+
|
|
30
|
+
# Run single test function
|
|
31
|
+
pytest tests/unit/test_openai.py::TestOpenAIProviderInit::test_basic_init -v
|
|
32
|
+
|
|
33
|
+
# Run tests by marker
|
|
34
|
+
pytest -m unit -v # Unit tests only (mocked)
|
|
35
|
+
pytest -m integration -v # Integration tests (real API calls)
|
|
36
|
+
pytest -m local -v # Local server tests
|
|
37
|
+
|
|
38
|
+
# Skip expensive tests
|
|
39
|
+
SKIP_LIVE_TESTS=true pytest tests/ -v
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Linting
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ruff check src/ && ruff format src/ --check # Check only
|
|
46
|
+
ruff check src/ --fix && ruff format src/ # Auto-fix
|
|
47
|
+
mypy src/ # Type checking
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Building
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python -m build # Build package
|
|
54
|
+
twine check dist/* # Verify package
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
### Provider Factory Pattern
|
|
60
|
+
|
|
61
|
+
All providers use a factory function with consistent interface:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from eq_chatbot_core.providers import get_provider
|
|
65
|
+
|
|
66
|
+
# Cloud providers
|
|
67
|
+
provider = get_provider("openai", api_key="sk-...")
|
|
68
|
+
provider = get_provider("anthropic", api_key="sk-ant-...")
|
|
69
|
+
provider = get_provider("langdock", api_key="ld-...")
|
|
70
|
+
provider = get_provider("openrouter", api_key="sk-or-...")
|
|
71
|
+
provider = get_provider("mammouth", api_key="mm-...")
|
|
72
|
+
provider = get_provider("litellm", api_key="...", base_url="https://gateway/v1") # OpenAI-compatible gateway
|
|
73
|
+
provider = get_provider("ionos", api_key="...") # IONOS AI Model Hub (EU-hosted, base_url has a default)
|
|
74
|
+
provider = get_provider("melious", api_key="sk-mel-...") # Melious.ai (sovereign EU-hosted, base_url has a default)
|
|
75
|
+
provider = get_provider("privatemode") # Privatemode.ai (end-to-end encrypted; needs the local privatemode-proxy on :8080)
|
|
76
|
+
|
|
77
|
+
# Local providers (LM Studio, Ollama)
|
|
78
|
+
provider = get_provider("lm_studio") # defaults to localhost:1234
|
|
79
|
+
provider = get_provider("ollama") # defaults to localhost:11434
|
|
80
|
+
provider = get_provider("local", base_url="http://custom:port/v1")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Provider Base Class
|
|
84
|
+
|
|
85
|
+
All providers inherit from `BaseLLMProvider` and must implement:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
class BaseLLMProvider(ABC):
|
|
89
|
+
provider_name: str # Property: "openai", "anthropic", etc.
|
|
90
|
+
default_model: str # Property: Default model ID
|
|
91
|
+
|
|
92
|
+
def chat_completion(messages, model, temperature, max_tokens, tools) -> LLMResponse
|
|
93
|
+
def stream_completion(messages, model, ...) -> Iterator[StreamChunk]
|
|
94
|
+
def list_models() -> list[dict]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Response Types
|
|
98
|
+
|
|
99
|
+
- `LLMResponse`: Complete response with content, token counts, tool_calls
|
|
100
|
+
- `StreamChunk`: Streaming delta with is_final flag and accumulated tool_calls
|
|
101
|
+
- `ModelInfo`: Model metadata (id, context_length, supports_vision, etc.)
|
|
102
|
+
|
|
103
|
+
### Exception Hierarchy
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
ProviderError (base)
|
|
107
|
+
├── RateLimitError # 429 errors, has retry_after
|
|
108
|
+
├── AuthenticationError # 401/403 errors
|
|
109
|
+
├── ContextLengthError # Token limit exceeded
|
|
110
|
+
└── OverloadedError # 529/503 transient errors (retryable)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Module Structure
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
src/eq_chatbot_core/
|
|
117
|
+
├── providers/ # LLM adapters
|
|
118
|
+
│ ├── base.py # BaseLLMProvider, response types, exceptions
|
|
119
|
+
│ ├── openai_provider.py # OpenAI
|
|
120
|
+
│ ├── anthropic_provider.py
|
|
121
|
+
│ ├── langdock_provider.py # LangDock gateway (EU/US regions)
|
|
122
|
+
│ ├── openrouter_provider.py # OpenRouter (400+ models)
|
|
123
|
+
│ ├── litellm_provider.py # LiteLLM / any OpenAI-compatible gateway
|
|
124
|
+
│ ├── ionos_provider.py # IONOS AI Model Hub (EU-hosted, OpenAI-compatible)
|
|
125
|
+
│ ├── melious_provider.py # Melious.ai (sovereign EU-hosted, OpenAI-compatible)
|
|
126
|
+
│ ├── privatemode_provider.py # Privatemode.ai (E2E-encrypted, via local attesting proxy)
|
|
127
|
+
│ └── local_provider.py # LM Studio, Ollama (OpenAI-compatible)
|
|
128
|
+
├── security/
|
|
129
|
+
│ ├── encryption.py # FernetEncryption for API key storage
|
|
130
|
+
│ ├── injection.py # Prompt injection detection
|
|
131
|
+
│ ├── rate_limit.py # Token bucket rate limiter
|
|
132
|
+
│ └── file_validator.py # MIME type validation (requires [security])
|
|
133
|
+
├── rag/
|
|
134
|
+
│ ├── chunker.py # Text chunking strategies
|
|
135
|
+
│ ├── embedder.py # Embedding generation
|
|
136
|
+
│ ├── retriever.py # Qdrant vector retrieval
|
|
137
|
+
│ └── context_manager.py # RAG context assembly
|
|
138
|
+
├── mcp/
|
|
139
|
+
│ ├── client.py # MCP client (legacy HTTP/SSE and stdio transports)
|
|
140
|
+
│ └── streamable_http.py # MCP client (Streamable HTTP, the current remote transport)
|
|
141
|
+
├── services/
|
|
142
|
+
│ ├── error_handler.py # Centralized error handling
|
|
143
|
+
│ └── knowledge_service.py # Knowledge export for vector DBs
|
|
144
|
+
├── utils/
|
|
145
|
+
│ ├── url_validation.py # SSRF / DNS-rebinding guard (shared by all providers)
|
|
146
|
+
│ └── pdf.py # PDF to image (requires [pdf])
|
|
147
|
+
├── cli.py # Click CLI: eq-chatbot
|
|
148
|
+
└── version.py # Version string
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Testing Patterns
|
|
152
|
+
|
|
153
|
+
### Unit Test Structure
|
|
154
|
+
|
|
155
|
+
Unit tests mock SDK clients at module level to avoid API calls:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
import sys
|
|
159
|
+
from unittest.mock import MagicMock
|
|
160
|
+
|
|
161
|
+
# Mock before importing provider
|
|
162
|
+
mock_openai_module = MagicMock()
|
|
163
|
+
sys.modules["openai"] = mock_openai_module
|
|
164
|
+
|
|
165
|
+
from eq_chatbot_core.providers.openai_provider import OpenAIProvider
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Test Markers
|
|
169
|
+
|
|
170
|
+
- `@pytest.mark.unit` - Mocked tests, no external dependencies
|
|
171
|
+
- `@pytest.mark.integration` - Real API calls, requires keys
|
|
172
|
+
- `@pytest.mark.local` - Requires running LM Studio/Ollama
|
|
173
|
+
- `@pytest.mark.expensive` - Uses expensive models, skipped in CI
|
|
174
|
+
- `@pytest.mark.slow` - Long-running tests
|
|
175
|
+
|
|
176
|
+
### Test Configuration
|
|
177
|
+
|
|
178
|
+
API keys come from `~/.config/eq-chatbot/config.toml` (`[providers.<name>].api_key`),
|
|
179
|
+
the same file the CLI uses. `conftest.py` mirrors them into `<NAME>_API_KEY` at
|
|
180
|
+
import; a real environment variable wins. `tests/.env.test` was removed in 3.1.0 —
|
|
181
|
+
it lived inside the repository, so the keys were one `git add -f` from publication.
|
|
182
|
+
|
|
183
|
+
- Keys: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `LANGDOCK_API_KEY`, `MAMMOUTH_API_KEY`, `IONOS_API_KEY`, `MELIOUS_API_KEY`, `PRIVATEMODE_API_KEY`, `LITELLM_API_KEY`, `OPENROUTER_API_KEY`
|
|
184
|
+
- `SKIP_LIVE_TESTS=true` - Skip integration tests (default false)
|
|
185
|
+
- `SKIP_LOCAL_TESTS=true` - Skip local server tests (default false)
|
|
186
|
+
- `<PROVIDER>_TEST_MODEL` - Override a model choice; defaults in `tests/model_registry.py`
|
|
187
|
+
|
|
188
|
+
### Fixtures (conftest.py)
|
|
189
|
+
|
|
190
|
+
- `test_config` - Session config from environment
|
|
191
|
+
- `mock_openai_client`, `mock_anthropic_client` - Pre-configured mocks
|
|
192
|
+
- `sample_messages`, `minimal_test_messages` - Test data
|
|
193
|
+
- `openai_provider`, `anthropic_provider` - Live provider instances
|
|
194
|
+
|
|
195
|
+
## Git Commit Conventions
|
|
196
|
+
|
|
197
|
+
- `[ADD]` - New features
|
|
198
|
+
- `[CHG]` - Modifications
|
|
199
|
+
- `[FIX]` - Bug fixes
|
|
200
|
+
|
|
201
|
+
## Dependencies
|
|
202
|
+
|
|
203
|
+
### Core (always installed)
|
|
204
|
+
|
|
205
|
+
openai (>=3), anthropic (>=1), httpx2, pydantic, cryptography, tiktoken, qdrant-client, click
|
|
206
|
+
|
|
207
|
+
One HTTP client library: `httpx2` (Pydantic's maintained continuation of httpx) carries this
|
|
208
|
+
library's own requests, the OpenAI SDK and — since anthropic 1.0.0 — the Anthropic SDK too.
|
|
209
|
+
The separate `httpx<1` dependency is gone; anthropic 1.0.0 rejects an httpx client with
|
|
210
|
+
`TypeError: Expected an instance of httpx2.Client`, so the floor is `>=1.0.0`.
|
|
211
|
+
`build_pinned_transport_for_url()` still takes an `http=` argument and works against either
|
|
212
|
+
module, which is why one guard implementation covers a future SDK that diverges again.
|
|
213
|
+
|
|
214
|
+
That release also removed `temperature`, `top_p` and `top_k` from `messages.create()` and
|
|
215
|
+
`messages.stream()`. Never write `params["temperature"]` for an Anthropic call — use
|
|
216
|
+
`apply_anthropic_temperature()` from `providers/temperature_constraints.py`, which clamps and
|
|
217
|
+
routes the value into `extra_body`.
|
|
218
|
+
|
|
219
|
+
### Optional
|
|
220
|
+
|
|
221
|
+
- `[dev]` - pytest, ruff, mypy, twine, pytest-cov, pytest-asyncio
|
|
222
|
+
- `[security]` - puremagic (MIME validation)
|
|
223
|
+
- `[pdf]` - pymupdf (PDF to image conversion)
|
|
224
|
+
- `[rag]` - qdrant-client (Qdrant vector retrieval; optional since v3.0.0)
|
|
225
|
+
- `[local]` - sentence-transformers (local embeddings)
|
|
@@ -5,6 +5,143 @@ All notable changes to eq-chatbot-core will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.3.0] - 2026-09-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **MCP client for the Streamable HTTP transport** (`mcp/streamable_http.py`,
|
|
13
|
+
`StreamableHTTPMCPClient`, factory `get_mcp_client(transport="http", ...)`). Until now the
|
|
14
|
+
library only spoke the HTTP+SSE transport of the 2024-11-05 spec (`GET /sse` plus a
|
|
15
|
+
server-announced POST URL). Current server SDKs - .NET, TypeScript, Python - offer Streamable
|
|
16
|
+
HTTP (spec 2025-03-26 and later) for remote operation: one POST endpoint such as
|
|
17
|
+
`http://host:5100/mcp`, answered with JSON or with an SSE stream. Against such a server the
|
|
18
|
+
old client ran into a connect timeout before it ever sent `initialize`. The new client reads
|
|
19
|
+
both answer shapes, carries a server-assigned `Mcp-Session-Id`, re-initialises once when the
|
|
20
|
+
server reports the session as gone (HTTP 404), and sends `MCP-Protocol-Version` after the
|
|
21
|
+
negotiation; stateless servers need none of that. `list_tools()` follows `nextCursor`
|
|
22
|
+
pagination; a tool reporting `isError: true` comes back as `success=False` with its text.
|
|
23
|
+
Same public surface as the SSE client, so callers switch without code changes. Verified
|
|
24
|
+
live against the official Python SDK 1.x in both modes (stateful with SSE answers, stateless
|
|
25
|
+
with JSON answers). `sse` and `stdio` are unchanged; the docs had promised
|
|
26
|
+
`transport="http"` all along, which the factory never accepted.
|
|
27
|
+
- **LAN mode for both HTTP transports** - `allow_private_ranges=True` on `get_mcp_client()`,
|
|
28
|
+
`MCPClient` and `StreamableHTTPMCPClient`. The SSRF check of the server URL always ran in
|
|
29
|
+
strict mode for MCP servers: private addresses, loopback and unresolvable hostnames were
|
|
30
|
+
rejected, so an MCP server on the company intranet was unreachable by construction even
|
|
31
|
+
though `validate_url()` has had a LAN mode for local LLM servers for a long time. In LAN
|
|
32
|
+
mode private and loopback targets become reachable and a hostname only the on-prem resolver
|
|
33
|
+
knows is accepted without an IP pin; link-local (cloud-metadata endpoint), reserved and
|
|
34
|
+
multicast addresses stay blocked. The SSE client applies the same mode to the POST URL it
|
|
35
|
+
receives in the `endpoint` event. Strict remains the default.
|
|
36
|
+
|
|
37
|
+
Tests: 1913 unit tests (52 new in `tests/unit/test_mcp_streamable_http.py`), mypy --strict clean.
|
|
38
|
+
|
|
39
|
+
## [3.2.1] - 2026-08-24
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **`.xlsx` conversion failed with an error about pandas.** The `[docs]` extra pulls
|
|
44
|
+
`markitdown[xlsx]`, which asks for a bare `openpyxl` and a bare `pandas` - no version on
|
|
45
|
+
either. The floor `openpyxl>=3.1.5` exists only inside pandas' own `excel` extra, and
|
|
46
|
+
markitdown does not request that extra, so nothing in the chain ever states it. A resolver
|
|
47
|
+
is therefore free to leave an older `openpyxl` in place, and Odoo pins exactly 3.1.2, so the
|
|
48
|
+
combination is not hypothetical - it is the normal case inside an Odoo install. Every
|
|
49
|
+
spreadsheet ended in:
|
|
50
|
+
|
|
51
|
+
XlsxConverter threw ImportError with message: Pandas requires version '3.1.5' or
|
|
52
|
+
newer of 'openpyxl' (version '3.1.2' currently installed).
|
|
53
|
+
|
|
54
|
+
Downstream that surfaces as "no text could be extracted", which names neither the library
|
|
55
|
+
nor the version. The `[docs]` extra now declares `openpyxl>=3.1.5,<4.0.0` itself.
|
|
56
|
+
|
|
57
|
+
Only `.xlsx` was affected - `.docx` goes through mammoth, `.pptx` through python-pptx and
|
|
58
|
+
`.pdf` through pymupdf, none of which route via pandas. Verified against all ten supported
|
|
59
|
+
extensions after the change.
|
|
60
|
+
|
|
61
|
+
## [3.2.0] - 2026-08-23
|
|
62
|
+
|
|
63
|
+
### Breaking
|
|
64
|
+
|
|
65
|
+
- **`anthropic` floor raised to >= 1.0.0; `httpx` is no longer a core dependency.**
|
|
66
|
+
The Anthropic SDK moved to `httpx2` in 1.0.0 and now rejects an `httpx` client outright
|
|
67
|
+
(`TypeError: Expected an instance of httpx2.Client`). Since `pyproject.toml` allowed
|
|
68
|
+
`<2.0.0`, a `pip install -U` would have silently broken the Anthropic provider. Both SDK
|
|
69
|
+
clients run on `httpx2` now, and the separate `httpx>=0.27,<1` dependency is gone — it
|
|
70
|
+
existed solely because of that one incompatibility.
|
|
71
|
+
|
|
72
|
+
The same SDK release **removed** `temperature`, `top_p` and `top_k` from `messages.create()`
|
|
73
|
+
and `messages.stream()`; passing one is a `TypeError`, not a warning. The SDK notes call this
|
|
74
|
+
"some minor breaking changes" — in practice every temperature-setting call failed. The new
|
|
75
|
+
helper `providers.temperature_constraints.apply_anthropic_temperature()` clamps the value and
|
|
76
|
+
routes it into `extra_body`. **Never write `params["temperature"]` for an Anthropic call again.**
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
|
|
80
|
+
- **GPT-5 temperature support is not uniform — every call to `gpt-5`, `gpt-5.5` and the whole
|
|
81
|
+
current `gpt-5.6` tier failed with HTTP 400.** The constraint table carried a single permissive
|
|
82
|
+
`gpt-5` prefix declaring the entire family temperature-capable. Measured live on 23.08.2026:
|
|
83
|
+
`gpt-5` and `gpt-5-mini` refuse ("Only the default (1) value is supported"), `gpt-5.1`/`5.2`/`5.4`
|
|
84
|
+
accept, `gpt-5.5` and `gpt-5.6` refuse again. The generic prefix now says **no** and the
|
|
85
|
+
accepting generations are listed individually, so an untested future generation inherits "no
|
|
86
|
+
temperature" — omitting it never breaks, sending it does.
|
|
87
|
+
|
|
88
|
+
- **`get_temperature_constraints()` fell through to the permissive default for a prefixed model.**
|
|
89
|
+
`openai/gpt-5.6-luna` matched no entry, got 0.0-2.0, and was therefore sent a parameter the model
|
|
90
|
+
rejects. The function now strips the provider prefix itself instead of trusting every caller to.
|
|
91
|
+
|
|
92
|
+
- **LangDock agents really stream now.** `_agent_stream_completion` sent `"stream": false` and
|
|
93
|
+
yielded the finished answer as a single chunk — no streaming, and LangDock aborts non-streaming
|
|
94
|
+
requests after 100 seconds with HTTP 524, which an agent with tools or knowledge search reaches
|
|
95
|
+
easily. Both agent paths now go through `_agent_request_events()` and the AI SDK 5 data-stream
|
|
96
|
+
protocol; the synchronous path streams internally and assembles the answer itself. The parser
|
|
97
|
+
splits on the `data: ` marker rather than on line boundaries (LangDock glues events together) and
|
|
98
|
+
skips unparseable blocks instead of letting one kill a running answer.
|
|
99
|
+
|
|
100
|
+
- **The LangDock Google backend was entirely broken.** `list_models()` served a hand-maintained
|
|
101
|
+
catalogue (`gemini-2.5-flash`, `gemini-2.5-pro`) the gateway had retired — including the backend
|
|
102
|
+
default model, so every call without an explicit model ended in 400. The list now comes live from
|
|
103
|
+
the same endpoint the error came from. On top of that the streaming path silently yielded
|
|
104
|
+
**nothing**: the parser looks for SSE lines, but without `?alt=sse` LangDock answers with a JSON
|
|
105
|
+
array — no match, no chunks, no error. The parameter is now set.
|
|
106
|
+
|
|
107
|
+
- **Google and Codestral errors swallowed their own cause.** Both logged the response body and then
|
|
108
|
+
called `raise_for_status()`, so the caller saw only "Client error '400 Bad Request'" while the
|
|
109
|
+
actual reason — a retired model, a missing entitlement — sat in a log line. The shared
|
|
110
|
+
`_raise_http_error()` now appends the body without losing the typed mapping (401 stays
|
|
111
|
+
`AuthenticationError`, 429 `RateLimitError`).
|
|
112
|
+
|
|
113
|
+
- **The Agent API's HTTP 400 is translated.** It covers three entirely different cases: agent
|
|
114
|
+
unknown, agent not shared with the API key, or no model resolvable at all. The last happens when
|
|
115
|
+
the agent's model is set to "Auto" in LangDock — the auto-router works only in the chat UI, and a
|
|
116
|
+
top-level `model` demonstrably does not override it. The message now names the remedy in plain
|
|
117
|
+
words.
|
|
118
|
+
|
|
119
|
+
### Changed
|
|
120
|
+
|
|
121
|
+
- **Every default model raised to the current generation.** Four were unusable or outdated:
|
|
122
|
+
`anthropic` pointed at `claude-sonnet-4-20250514` and `melious` at `minimax-428b-m3` — both
|
|
123
|
+
withdrawn — while `openai`, `mammouth` and `openrouter` still named `gpt-4o`. Now:
|
|
124
|
+
`gpt-5.6-luna`, `claude-sonnet-5`, `openai/gpt-5.6-luna`, `nemotron-3-nano-30b-a3b`. Three of
|
|
125
|
+
LangDock's four backend defaults were dead (`gpt-4o`, `claude-sonnet-4-20250514`,
|
|
126
|
+
`gemini-2.5-flash`).
|
|
127
|
+
|
|
128
|
+
A default model is the one unavoidable constant — everything else is resolved live. The new live
|
|
129
|
+
guards `TestProviderDefaultsAreLive` and `TestLangDockDefaultsAreLive` fail the moment a provider
|
|
130
|
+
stops serving one, and additionally check that it actually answers: being listed is not enough.
|
|
131
|
+
|
|
132
|
+
- **Dependency floors raised to the current release** (21 entries), among them
|
|
133
|
+
`sentence-transformers` 3 -> 6, `websockets` 13 -> 17.0.1, `pytest-asyncio` 0.24 -> 1.4,
|
|
134
|
+
`pytest` 8 -> 9.1.1, `mypy` 1.15 -> 2.3.1, `pytest-cov` 6 -> 7.1, `qdrant-client` 1.12 -> 1.19,
|
|
135
|
+
`fastapi` 0.115 -> 0.141, `uvicorn` 0.32 -> 0.52. An old floor is an open vulnerability waiting
|
|
136
|
+
for a fresh install. The suite runs green on all new majors without a code change.
|
|
137
|
+
|
|
138
|
+
- **Live coverage now spans all LangDock backends.** Only `openai` and `anthropic` were tested;
|
|
139
|
+
the Google backend speaks an entirely different protocol and was never touched live — which is
|
|
140
|
+
precisely why its defect went unnoticed. The Codestral path has no coverage any more: Mistral is
|
|
141
|
+
not of interest via LangDock.
|
|
142
|
+
|
|
143
|
+
**Test suite:** 1858 unit tests, 51 live tests (0 failures), mypy `--strict` clean.
|
|
144
|
+
|
|
8
145
|
## [3.1.1] - 2026-08-20
|
|
9
146
|
|
|
10
147
|
### Fixed
|
|
@@ -136,7 +136,8 @@ src/eq_chatbot_core/
|
|
|
136
136
|
│ ├── retriever.py # Qdrant vector retrieval
|
|
137
137
|
│ └── context_manager.py # RAG context assembly
|
|
138
138
|
├── mcp/
|
|
139
|
-
│
|
|
139
|
+
│ ├── client.py # MCP client (legacy HTTP/SSE and stdio transports)
|
|
140
|
+
│ └── streamable_http.py # MCP client (Streamable HTTP, the current remote transport)
|
|
140
141
|
├── services/
|
|
141
142
|
│ ├── error_handler.py # Centralized error handling
|
|
142
143
|
│ └── knowledge_service.py # Knowledge export for vector DBs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: eq-chatbot-core
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: Core library for LLM chatbot integration with multi-provider support (OpenAI, Anthropic, LangDock, OpenRouter, Mammouth, Azure, Vertex AI, LiteLLM, IONOS, Local)
|
|
5
5
|
Project-URL: Homepage, https://www.ownerp.com
|
|
6
6
|
Project-URL: Documentation, https://github.com/equitania/eq-chatbot-core
|
|
@@ -39,6 +39,7 @@ Requires-Dist: ruff<1.0.0,>=0.16.4; extra == 'dev'
|
|
|
39
39
|
Requires-Dist: twine<8.0.0,>=7.0.0; extra == 'dev'
|
|
40
40
|
Provides-Extra: docs
|
|
41
41
|
Requires-Dist: markitdown[docx,pdf,pptx,xlsx]<0.2.0,>=0.1.7; extra == 'docs'
|
|
42
|
+
Requires-Dist: openpyxl<4.0.0,>=3.1.5; extra == 'docs'
|
|
42
43
|
Requires-Dist: pymupdf<2.0.0,>=1.28.2; extra == 'docs'
|
|
43
44
|
Provides-Extra: image
|
|
44
45
|
Requires-Dist: pillow<13.0,>=12.3.0; extra == 'image'
|
|
@@ -85,7 +86,7 @@ Originally extracted from an Odoo 18 chatbot integration; works standalone witho
|
|
|
85
86
|
- **Temperature Safety** — automatic model-specific temperature clamping
|
|
86
87
|
- **Security** — Fernet encryption, prompt-injection detection (direct user input + indirect tool/RAG content), file-upload validation, race-free token-bucket rate limiting
|
|
87
88
|
- **RAG Pipeline** — chunking, embeddings (incl. Melious.ai and IONOS embedders), Qdrant-backed retrieval, context-window management
|
|
88
|
-
- **MCP Client** — HTTP/SSE and stdio transports, hardened against DNS rebinding, SSRF, and subprocess env injection
|
|
89
|
+
- **MCP Client** — Streamable HTTP, legacy HTTP/SSE and stdio transports, hardened against DNS rebinding, SSRF, and subprocess env injection; LAN mode for servers on the intranet
|
|
89
90
|
- **CLI Tool** — provider testing, model discovery, programmatic JSON I/O chat
|
|
90
91
|
- **Text-to-Image Generation** (v1.14.0) — `eq-chatbot image` (single PNG) and `eq-chatbot listing-assets` (batch from a recipe); OpenAI `gpt-image-1` and OpenRouter image models
|
|
91
92
|
- **HTTP/SSE Server Mode** (v1.7.0) — run as a local sidecar (`eq-chatbot serve`) for cross-language integrations (Avalonia/.NET, Electron, native mobile)
|
|
@@ -162,7 +163,7 @@ For more — streaming, other providers, error handling — see [docs/providers.
|
|
|
162
163
|
| CLI commands | [docs/cli.md](docs/cli.md#english) |
|
|
163
164
|
| HTTP/SSE server mode | [docs/server-mode.md](docs/server-mode.md#english) |
|
|
164
165
|
| Security (encryption, injection, files, rate limit) | [docs/security.md](docs/security.md#english) |
|
|
165
|
-
| MCP client (HTTP
|
|
166
|
+
| MCP client (Streamable HTTP + SSE + stdio) | [docs/mcp.md](docs/mcp.md#english) |
|
|
166
167
|
| RAG pipeline (chunking, embedding, retrieval) | [docs/rag.md](docs/rag.md#english) |
|
|
167
168
|
| Testing (markers, integration setup, cost-aware patterns) | [docs/testing.md](docs/testing.md) |
|
|
168
169
|
|
|
@@ -289,7 +290,7 @@ Ursprünglich aus einer Odoo-18-Chatbot-Integration extrahiert; funktioniert sta
|
|
|
289
290
|
- **Temperature-Sicherheit** — automatisches modellspezifisches Temperature-Clamping
|
|
290
291
|
- **Sicherheit** — Fernet-Verschlüsselung, Prompt-Injection-Erkennung (direkte Nutzereingaben + indirekte Tool-/RAG-Inhalte), File-Upload-Validierung, Race-freies Token-Bucket-Rate-Limiting
|
|
291
292
|
- **RAG-Pipeline** — Chunking, Embeddings (inkl. Melious.ai- und IONOS-Embedder), Qdrant-basiertes Retrieval, Context-Window-Management
|
|
292
|
-
- **MCP-Client** — HTTP/SSE und stdio Transports, gehärtet gegen DNS-Rebinding, SSRF und Subprocess-Env-Injection
|
|
293
|
+
- **MCP-Client** — Streamable HTTP, älteres HTTP/SSE und stdio als Transports, gehärtet gegen DNS-Rebinding, SSRF und Subprocess-Env-Injection; LAN-Modus für Server im Intranet
|
|
293
294
|
- **CLI-Tool** — Provider-Tests, Modell-Discovery, programmatische JSON-I/O-Chat-Calls
|
|
294
295
|
- **Text-zu-Bild-Generierung** (v1.14.0) — `eq-chatbot image` (einzelnes PNG) und `eq-chatbot listing-assets` (Batch aus einer Recipe); OpenAI `gpt-image-1` und OpenRouter-Bildmodelle
|
|
295
296
|
- **HTTP/SSE-Server-Mode** (v1.7.0) — lokaler Sidecar (`eq-chatbot serve`) für Cross-Language-Integrationen (Avalonia/.NET, Electron, native Mobile)
|
|
@@ -370,7 +371,7 @@ Für mehr — Streaming, andere Provider, Error-Handling — siehe [docs/provide
|
|
|
370
371
|
| CLI-Befehle | [docs/cli.md](docs/cli.md#deutsch) |
|
|
371
372
|
| HTTP/SSE-Server-Mode | [docs/server-mode.md](docs/server-mode.md#deutsch) |
|
|
372
373
|
| Security (Verschlüsselung, Injection, Files, Rate-Limit) | [docs/security.md](docs/security.md#deutsch) |
|
|
373
|
-
| MCP-Client (HTTP
|
|
374
|
+
| MCP-Client (Streamable HTTP + SSE + stdio) | [docs/mcp.md](docs/mcp.md#deutsch) |
|
|
374
375
|
| RAG-Pipeline (Chunking, Embedding, Retrieval) | [docs/rag.md](docs/rag.md#deutsch) |
|
|
375
376
|
| Testing (Marker, Integration-Setup, Cost-Aware-Patterns) | [docs/testing.md](docs/testing.md) |
|
|
376
377
|
|
|
@@ -25,7 +25,7 @@ Originally extracted from an Odoo 18 chatbot integration; works standalone witho
|
|
|
25
25
|
- **Temperature Safety** — automatic model-specific temperature clamping
|
|
26
26
|
- **Security** — Fernet encryption, prompt-injection detection (direct user input + indirect tool/RAG content), file-upload validation, race-free token-bucket rate limiting
|
|
27
27
|
- **RAG Pipeline** — chunking, embeddings (incl. Melious.ai and IONOS embedders), Qdrant-backed retrieval, context-window management
|
|
28
|
-
- **MCP Client** — HTTP/SSE and stdio transports, hardened against DNS rebinding, SSRF, and subprocess env injection
|
|
28
|
+
- **MCP Client** — Streamable HTTP, legacy HTTP/SSE and stdio transports, hardened against DNS rebinding, SSRF, and subprocess env injection; LAN mode for servers on the intranet
|
|
29
29
|
- **CLI Tool** — provider testing, model discovery, programmatic JSON I/O chat
|
|
30
30
|
- **Text-to-Image Generation** (v1.14.0) — `eq-chatbot image` (single PNG) and `eq-chatbot listing-assets` (batch from a recipe); OpenAI `gpt-image-1` and OpenRouter image models
|
|
31
31
|
- **HTTP/SSE Server Mode** (v1.7.0) — run as a local sidecar (`eq-chatbot serve`) for cross-language integrations (Avalonia/.NET, Electron, native mobile)
|
|
@@ -102,7 +102,7 @@ For more — streaming, other providers, error handling — see [docs/providers.
|
|
|
102
102
|
| CLI commands | [docs/cli.md](docs/cli.md#english) |
|
|
103
103
|
| HTTP/SSE server mode | [docs/server-mode.md](docs/server-mode.md#english) |
|
|
104
104
|
| Security (encryption, injection, files, rate limit) | [docs/security.md](docs/security.md#english) |
|
|
105
|
-
| MCP client (HTTP
|
|
105
|
+
| MCP client (Streamable HTTP + SSE + stdio) | [docs/mcp.md](docs/mcp.md#english) |
|
|
106
106
|
| RAG pipeline (chunking, embedding, retrieval) | [docs/rag.md](docs/rag.md#english) |
|
|
107
107
|
| Testing (markers, integration setup, cost-aware patterns) | [docs/testing.md](docs/testing.md) |
|
|
108
108
|
|
|
@@ -229,7 +229,7 @@ Ursprünglich aus einer Odoo-18-Chatbot-Integration extrahiert; funktioniert sta
|
|
|
229
229
|
- **Temperature-Sicherheit** — automatisches modellspezifisches Temperature-Clamping
|
|
230
230
|
- **Sicherheit** — Fernet-Verschlüsselung, Prompt-Injection-Erkennung (direkte Nutzereingaben + indirekte Tool-/RAG-Inhalte), File-Upload-Validierung, Race-freies Token-Bucket-Rate-Limiting
|
|
231
231
|
- **RAG-Pipeline** — Chunking, Embeddings (inkl. Melious.ai- und IONOS-Embedder), Qdrant-basiertes Retrieval, Context-Window-Management
|
|
232
|
-
- **MCP-Client** — HTTP/SSE und stdio Transports, gehärtet gegen DNS-Rebinding, SSRF und Subprocess-Env-Injection
|
|
232
|
+
- **MCP-Client** — Streamable HTTP, älteres HTTP/SSE und stdio als Transports, gehärtet gegen DNS-Rebinding, SSRF und Subprocess-Env-Injection; LAN-Modus für Server im Intranet
|
|
233
233
|
- **CLI-Tool** — Provider-Tests, Modell-Discovery, programmatische JSON-I/O-Chat-Calls
|
|
234
234
|
- **Text-zu-Bild-Generierung** (v1.14.0) — `eq-chatbot image` (einzelnes PNG) und `eq-chatbot listing-assets` (Batch aus einer Recipe); OpenAI `gpt-image-1` und OpenRouter-Bildmodelle
|
|
235
235
|
- **HTTP/SSE-Server-Mode** (v1.7.0) — lokaler Sidecar (`eq-chatbot serve`) für Cross-Language-Integrationen (Avalonia/.NET, Electron, native Mobile)
|
|
@@ -310,7 +310,7 @@ Für mehr — Streaming, andere Provider, Error-Handling — siehe [docs/provide
|
|
|
310
310
|
| CLI-Befehle | [docs/cli.md](docs/cli.md#deutsch) |
|
|
311
311
|
| HTTP/SSE-Server-Mode | [docs/server-mode.md](docs/server-mode.md#deutsch) |
|
|
312
312
|
| Security (Verschlüsselung, Injection, Files, Rate-Limit) | [docs/security.md](docs/security.md#deutsch) |
|
|
313
|
-
| MCP-Client (HTTP
|
|
313
|
+
| MCP-Client (Streamable HTTP + SSE + stdio) | [docs/mcp.md](docs/mcp.md#deutsch) |
|
|
314
314
|
| RAG-Pipeline (Chunking, Embedding, Retrieval) | [docs/rag.md](docs/rag.md#deutsch) |
|
|
315
315
|
| Testing (Marker, Integration-Setup, Cost-Aware-Patterns) | [docs/testing.md](docs/testing.md) |
|
|
316
316
|
|
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## Version 3.3.0 (08.09.2026)
|
|
4
|
+
|
|
5
|
+
### [ADD]
|
|
6
|
+
|
|
7
|
+
- **MCP-Client für den Streamable-HTTP-Transport** (`mcp/streamable_http.py`,
|
|
8
|
+
`StreamableHTTPMCPClient`, Fabrik `get_mcp_client(transport="http", …)`). Bisher sprach die
|
|
9
|
+
Bibliothek nur den HTTP+SSE-Transport der Spec 2024-11-05 (`GET /sse`, vom Server gemeldete
|
|
10
|
+
POST-URL). Aktuelle Server-SDKs — .NET, TypeScript, Python — bieten für den Fernbetrieb
|
|
11
|
+
Streamable HTTP (Spec ab 2025-03-26): ein einziger POST-Endpunkt wie `http://host:5100/mcp`,
|
|
12
|
+
Antwort als JSON oder als SSE-Stream. Gegen so einen Server lief der alte Client in einen
|
|
13
|
+
Verbindungs-Timeout, bevor überhaupt ein `initialize` gesendet wurde. Der neue Client
|
|
14
|
+
liest beide Antwortformen, führt eine vom Server vergebene `Mcp-Session-Id` mit und
|
|
15
|
+
initialisiert einmal neu, wenn der Server die Sitzung mit HTTP 404 als abgelaufen meldet;
|
|
16
|
+
zustandslose Server brauchen nichts davon. Nach der Aushandlung sendet er
|
|
17
|
+
`MCP-Protocol-Version`. `list_tools()` folgt der `nextCursor`-Blätterung; ein Werkzeug mit
|
|
18
|
+
`isError: true` kommt als `success=False` samt Text zurück. Öffentliche Oberfläche wie beim
|
|
19
|
+
SSE-Client, daher ohne Änderung am Aufrufer austauschbar. Live geprüft gegen das offizielle
|
|
20
|
+
Python-SDK 1.x in beiden Betriebsarten (zustandsbehaftet mit SSE-Antworten, zustandslos mit
|
|
21
|
+
JSON-Antworten). Die Transportnamen `sse` und `stdio` bleiben unverändert; die Doku hatte
|
|
22
|
+
bereits `transport="http"` versprochen, das die Fabrik bis jetzt gar nicht kannte.
|
|
23
|
+
- **LAN-Modus für beide HTTP-Transports** — `allow_private_ranges=True` an
|
|
24
|
+
`get_mcp_client()`, `MCPClient` und `StreamableHTTPMCPClient`. Die SSRF-Prüfung der
|
|
25
|
+
Server-URL lief für MCP-Server immer im strikten Modus: private Adressen, Loopback und
|
|
26
|
+
nicht auflösbare Hostnamen wurden abgewiesen — ein MCP-Server im Firmen-Intranet war damit
|
|
27
|
+
grundsätzlich unerreichbar, obwohl `validate_url()` den LAN-Modus für lokale LLM-Server
|
|
28
|
+
längst kannte. Im LAN-Modus werden private und Loopback-Ziele erreichbar und ein nur intern
|
|
29
|
+
auflösbarer Hostname ohne IP-Pin akzeptiert; Link-Local (Cloud-Metadaten-Endpunkt),
|
|
30
|
+
reservierte und Multicast-Adressen bleiben gesperrt. Beim SSE-Client gilt derselbe Modus
|
|
31
|
+
auch für die im `endpoint`-Event gemeldete POST-URL. Standard bleibt strikt.
|
|
32
|
+
|
|
33
|
+
Tests: 1913 Unit-Tests (52 neu in `tests/unit/test_mcp_streamable_http.py`), mypy --strict
|
|
34
|
+
sauber.
|
|
35
|
+
|
|
36
|
+
## Version 3.2.1 (24.08.2026)
|
|
37
|
+
|
|
38
|
+
### [FIX]
|
|
39
|
+
|
|
40
|
+
- **Die Umwandlung von `.xlsx` scheiterte mit einer Meldung über pandas.** Das `[docs]`-Extra
|
|
41
|
+
zieht `markitdown[xlsx]`, und das verlangt schlicht „openpyxl" und „pandas" — ohne jede
|
|
42
|
+
Fassungsangabe. Die Untergrenze `openpyxl>=3.1.5` steht allein im `excel`-Extra von pandas,
|
|
43
|
+
das markitdown nicht anfordert; in der ganzen Kette nennt sie also niemand. Ein Auflöser darf
|
|
44
|
+
damit eine ältere Fassung stehen lassen, und Odoo legt sich genau auf 3.1.2 fest — die
|
|
45
|
+
Kombination ist demnach kein Sonderfall, sondern der Regelfall in einer Odoo-Installation.
|
|
46
|
+
Jede Tabellenkalkulation endete mit:
|
|
47
|
+
|
|
48
|
+
XlsxConverter threw ImportError with message: Pandas requires version '3.1.5' or
|
|
49
|
+
newer of 'openpyxl' (version '3.1.2' currently installed).
|
|
50
|
+
|
|
51
|
+
Beim Aufrufer kommt davon „Es konnte kein Text extrahiert werden" an — eine Meldung, die
|
|
52
|
+
weder die Bibliothek noch die Fassung nennt. Das `[docs]`-Extra verlangt jetzt selbst
|
|
53
|
+
`openpyxl>=3.1.5,<4.0.0`.
|
|
54
|
+
|
|
55
|
+
Betroffen war ausschließlich `.xlsx`: `.docx` läuft über mammoth, `.pptx` über python-pptx
|
|
56
|
+
und `.pdf` über pymupdf, keines davon über pandas. Nach der Änderung gegen alle zehn
|
|
57
|
+
unterstützten Endungen geprüft.
|
|
58
|
+
|
|
3
59
|
## Version 3.2.0 (23.08.2026)
|
|
4
60
|
|
|
5
61
|
### ⚠️ Breaking
|