PraisonAI 3.0.0__py3-none-any.whl
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.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Redis Vector implementation of KnowledgeStore.
|
|
3
|
+
|
|
4
|
+
Requires: redis
|
|
5
|
+
Install: pip install redis
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from .base import KnowledgeStore, KnowledgeDocument
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RedisVectorKnowledgeStore(KnowledgeStore):
|
|
18
|
+
"""
|
|
19
|
+
Redis-based knowledge store using RediSearch for vector search.
|
|
20
|
+
|
|
21
|
+
Requires Redis Stack or Redis with RediSearch module.
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
store = RedisVectorKnowledgeStore(
|
|
25
|
+
url="redis://localhost:6379"
|
|
26
|
+
)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
url: Optional[str] = None,
|
|
32
|
+
host: str = "localhost",
|
|
33
|
+
port: int = 6379,
|
|
34
|
+
password: Optional[str] = None,
|
|
35
|
+
prefix: str = "praison:vec:",
|
|
36
|
+
):
|
|
37
|
+
try:
|
|
38
|
+
import redis
|
|
39
|
+
from redis.commands.search.field import VectorField, TextField, NumericField
|
|
40
|
+
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
|
|
41
|
+
from redis.commands.search.query import Query
|
|
42
|
+
except ImportError:
|
|
43
|
+
raise ImportError(
|
|
44
|
+
"redis is required for Redis Vector support. "
|
|
45
|
+
"Install with: pip install redis"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
self._redis = redis
|
|
49
|
+
self._VectorField = VectorField
|
|
50
|
+
self._TextField = TextField
|
|
51
|
+
self._NumericField = NumericField
|
|
52
|
+
self._IndexDefinition = IndexDefinition
|
|
53
|
+
self._IndexType = IndexType
|
|
54
|
+
self._Query = Query
|
|
55
|
+
|
|
56
|
+
self.prefix = prefix
|
|
57
|
+
|
|
58
|
+
if url:
|
|
59
|
+
self._client = redis.from_url(url, decode_responses=False)
|
|
60
|
+
else:
|
|
61
|
+
self._client = redis.Redis(
|
|
62
|
+
host=host, port=port, password=password, decode_responses=False
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
self._client.ping()
|
|
66
|
+
logger.info(f"Connected to Redis at {url or f'{host}:{port}'}")
|
|
67
|
+
|
|
68
|
+
def _index_name(self, collection: str) -> str:
|
|
69
|
+
return f"{self.prefix}{collection}:idx"
|
|
70
|
+
|
|
71
|
+
def _doc_key(self, collection: str, doc_id: str) -> str:
|
|
72
|
+
return f"{self.prefix}{collection}:{doc_id}"
|
|
73
|
+
|
|
74
|
+
def create_collection(
|
|
75
|
+
self,
|
|
76
|
+
name: str,
|
|
77
|
+
dimension: int,
|
|
78
|
+
distance: str = "cosine",
|
|
79
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Create a new index."""
|
|
82
|
+
index_name = self._index_name(name)
|
|
83
|
+
|
|
84
|
+
distance_map = {"cosine": "COSINE", "euclidean": "L2", "dot": "IP"}
|
|
85
|
+
|
|
86
|
+
schema = (
|
|
87
|
+
self._TextField("content"),
|
|
88
|
+
self._TextField("content_hash"),
|
|
89
|
+
self._NumericField("created_at"),
|
|
90
|
+
self._VectorField(
|
|
91
|
+
"embedding",
|
|
92
|
+
"HNSW",
|
|
93
|
+
{
|
|
94
|
+
"TYPE": "FLOAT32",
|
|
95
|
+
"DIM": dimension,
|
|
96
|
+
"DISTANCE_METRIC": distance_map.get(distance, "COSINE"),
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
definition = self._IndexDefinition(
|
|
102
|
+
prefix=[f"{self.prefix}{name}:"],
|
|
103
|
+
index_type=self._IndexType.HASH,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
self._client.ft(index_name).create_index(schema, definition=definition)
|
|
108
|
+
logger.info(f"Created Redis index: {index_name}")
|
|
109
|
+
except Exception as e:
|
|
110
|
+
if "Index already exists" not in str(e):
|
|
111
|
+
raise
|
|
112
|
+
|
|
113
|
+
def delete_collection(self, name: str) -> bool:
|
|
114
|
+
"""Delete an index and all documents."""
|
|
115
|
+
index_name = self._index_name(name)
|
|
116
|
+
try:
|
|
117
|
+
self._client.ft(index_name).dropindex(delete_documents=True)
|
|
118
|
+
return True
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.warning(f"Failed to delete index {index_name}: {e}")
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
def collection_exists(self, name: str) -> bool:
|
|
124
|
+
"""Check if an index exists."""
|
|
125
|
+
index_name = self._index_name(name)
|
|
126
|
+
try:
|
|
127
|
+
self._client.ft(index_name).info()
|
|
128
|
+
return True
|
|
129
|
+
except Exception:
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
def list_collections(self) -> List[str]:
|
|
133
|
+
"""List all collections."""
|
|
134
|
+
try:
|
|
135
|
+
indexes = self._client.execute_command("FT._LIST")
|
|
136
|
+
prefix = f"{self.prefix}"
|
|
137
|
+
return [
|
|
138
|
+
idx.decode().replace(prefix, "").replace(":idx", "")
|
|
139
|
+
for idx in indexes
|
|
140
|
+
if idx.decode().startswith(prefix)
|
|
141
|
+
]
|
|
142
|
+
except Exception:
|
|
143
|
+
return []
|
|
144
|
+
|
|
145
|
+
def insert(
|
|
146
|
+
self,
|
|
147
|
+
collection: str,
|
|
148
|
+
documents: List[KnowledgeDocument]
|
|
149
|
+
) -> List[str]:
|
|
150
|
+
"""Insert documents."""
|
|
151
|
+
import numpy as np
|
|
152
|
+
|
|
153
|
+
ids = []
|
|
154
|
+
pipe = self._client.pipeline()
|
|
155
|
+
|
|
156
|
+
for doc in documents:
|
|
157
|
+
if doc.embedding is None:
|
|
158
|
+
raise ValueError(f"Document {doc.id} has no embedding")
|
|
159
|
+
|
|
160
|
+
key = self._doc_key(collection, doc.id)
|
|
161
|
+
embedding_bytes = np.array(doc.embedding, dtype=np.float32).tobytes()
|
|
162
|
+
|
|
163
|
+
pipe.hset(key, mapping={
|
|
164
|
+
"content": doc.content,
|
|
165
|
+
"content_hash": doc.content_hash or "",
|
|
166
|
+
"created_at": doc.created_at,
|
|
167
|
+
"embedding": embedding_bytes,
|
|
168
|
+
})
|
|
169
|
+
ids.append(doc.id)
|
|
170
|
+
|
|
171
|
+
pipe.execute()
|
|
172
|
+
return ids
|
|
173
|
+
|
|
174
|
+
def upsert(
|
|
175
|
+
self,
|
|
176
|
+
collection: str,
|
|
177
|
+
documents: List[KnowledgeDocument]
|
|
178
|
+
) -> List[str]:
|
|
179
|
+
"""Insert or update documents."""
|
|
180
|
+
return self.insert(collection, documents)
|
|
181
|
+
|
|
182
|
+
def search(
|
|
183
|
+
self,
|
|
184
|
+
collection: str,
|
|
185
|
+
query_embedding: List[float],
|
|
186
|
+
limit: int = 5,
|
|
187
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
188
|
+
score_threshold: Optional[float] = None
|
|
189
|
+
) -> List[KnowledgeDocument]:
|
|
190
|
+
"""Search for similar documents."""
|
|
191
|
+
import numpy as np
|
|
192
|
+
|
|
193
|
+
index_name = self._index_name(collection)
|
|
194
|
+
query_bytes = np.array(query_embedding, dtype=np.float32).tobytes()
|
|
195
|
+
|
|
196
|
+
query_str = f"*=>[KNN {limit} @embedding $vec AS score]"
|
|
197
|
+
|
|
198
|
+
query = (
|
|
199
|
+
self._Query(query_str)
|
|
200
|
+
.return_fields("content", "content_hash", "created_at", "score")
|
|
201
|
+
.sort_by("score")
|
|
202
|
+
.dialect(2)
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
results = self._client.ft(index_name).search(
|
|
206
|
+
query, query_params={"vec": query_bytes}
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
documents = []
|
|
210
|
+
for doc in results.docs:
|
|
211
|
+
score = float(doc.score) if hasattr(doc, "score") else 0
|
|
212
|
+
# Redis returns distance, convert to similarity for cosine
|
|
213
|
+
similarity = 1 - score
|
|
214
|
+
|
|
215
|
+
if score_threshold and similarity < score_threshold:
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
doc_id = doc.id.decode() if isinstance(doc.id, bytes) else doc.id
|
|
219
|
+
doc_id = doc_id.split(":")[-1] # Remove prefix
|
|
220
|
+
|
|
221
|
+
knowledge_doc = KnowledgeDocument(
|
|
222
|
+
id=doc_id,
|
|
223
|
+
content=doc.content.decode() if isinstance(doc.content, bytes) else doc.content,
|
|
224
|
+
embedding=None,
|
|
225
|
+
metadata={},
|
|
226
|
+
content_hash=doc.content_hash.decode() if isinstance(doc.content_hash, bytes) else doc.content_hash,
|
|
227
|
+
created_at=float(doc.created_at) if doc.created_at else 0,
|
|
228
|
+
)
|
|
229
|
+
documents.append(knowledge_doc)
|
|
230
|
+
|
|
231
|
+
return documents
|
|
232
|
+
|
|
233
|
+
def get(
|
|
234
|
+
self,
|
|
235
|
+
collection: str,
|
|
236
|
+
ids: List[str]
|
|
237
|
+
) -> List[KnowledgeDocument]:
|
|
238
|
+
"""Get documents by IDs."""
|
|
239
|
+
import numpy as np
|
|
240
|
+
|
|
241
|
+
documents = []
|
|
242
|
+
for doc_id in ids:
|
|
243
|
+
key = self._doc_key(collection, doc_id)
|
|
244
|
+
data = self._client.hgetall(key)
|
|
245
|
+
|
|
246
|
+
if data:
|
|
247
|
+
embedding = None
|
|
248
|
+
if b"embedding" in data:
|
|
249
|
+
embedding = np.frombuffer(data[b"embedding"], dtype=np.float32).tolist()
|
|
250
|
+
|
|
251
|
+
doc = KnowledgeDocument(
|
|
252
|
+
id=doc_id,
|
|
253
|
+
content=data.get(b"content", b"").decode(),
|
|
254
|
+
embedding=embedding,
|
|
255
|
+
metadata={},
|
|
256
|
+
content_hash=data.get(b"content_hash", b"").decode(),
|
|
257
|
+
created_at=float(data.get(b"created_at", 0)),
|
|
258
|
+
)
|
|
259
|
+
documents.append(doc)
|
|
260
|
+
|
|
261
|
+
return documents
|
|
262
|
+
|
|
263
|
+
def delete(
|
|
264
|
+
self,
|
|
265
|
+
collection: str,
|
|
266
|
+
ids: Optional[List[str]] = None,
|
|
267
|
+
filters: Optional[Dict[str, Any]] = None
|
|
268
|
+
) -> int:
|
|
269
|
+
"""Delete documents."""
|
|
270
|
+
if ids:
|
|
271
|
+
pipe = self._client.pipeline()
|
|
272
|
+
for doc_id in ids:
|
|
273
|
+
key = self._doc_key(collection, doc_id)
|
|
274
|
+
pipe.delete(key)
|
|
275
|
+
pipe.execute()
|
|
276
|
+
return len(ids)
|
|
277
|
+
return 0
|
|
278
|
+
|
|
279
|
+
def count(self, collection: str) -> int:
|
|
280
|
+
"""Count documents."""
|
|
281
|
+
index_name = self._index_name(collection)
|
|
282
|
+
try:
|
|
283
|
+
info = self._client.ft(index_name).info()
|
|
284
|
+
return int(info.get("num_docs", 0))
|
|
285
|
+
except Exception:
|
|
286
|
+
return 0
|
|
287
|
+
|
|
288
|
+
def close(self) -> None:
|
|
289
|
+
"""Close the store."""
|
|
290
|
+
if self._client:
|
|
291
|
+
self._client.close()
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SingleStore Vector implementation of KnowledgeStore.
|
|
3
|
+
|
|
4
|
+
Requires: singlestoredb
|
|
5
|
+
Install: pip install singlestoredb
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from .base import KnowledgeStore, KnowledgeDocument
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SingleStoreVectorKnowledgeStore(KnowledgeStore):
|
|
17
|
+
"""
|
|
18
|
+
SingleStore vector store for knowledge/RAG.
|
|
19
|
+
|
|
20
|
+
Uses SingleStore's native vector capabilities.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
store = SingleStoreVectorKnowledgeStore(
|
|
24
|
+
url="singlestoredb://user:pass@host:3306/db"
|
|
25
|
+
)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
url: Optional[str] = None,
|
|
31
|
+
host: str = "localhost",
|
|
32
|
+
port: int = 3306,
|
|
33
|
+
database: str = "praisonai",
|
|
34
|
+
user: str = "root",
|
|
35
|
+
password: str = "",
|
|
36
|
+
table_prefix: str = "praisonai_",
|
|
37
|
+
embedding_dim: int = 1536,
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
Initialize SingleStore Vector store.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
url: SingleStore connection URL
|
|
44
|
+
host: Database host
|
|
45
|
+
port: Database port
|
|
46
|
+
database: Database name
|
|
47
|
+
user: Database user
|
|
48
|
+
password: Database password
|
|
49
|
+
table_prefix: Prefix for table names
|
|
50
|
+
embedding_dim: Embedding dimension
|
|
51
|
+
"""
|
|
52
|
+
self.url = url
|
|
53
|
+
self.host = host
|
|
54
|
+
self.port = port
|
|
55
|
+
self.database = database
|
|
56
|
+
self.user = user
|
|
57
|
+
self.password = password
|
|
58
|
+
self.table_prefix = table_prefix
|
|
59
|
+
self.embedding_dim = embedding_dim
|
|
60
|
+
|
|
61
|
+
self._conn = None
|
|
62
|
+
self._initialized = False
|
|
63
|
+
|
|
64
|
+
def _init_client(self):
|
|
65
|
+
"""Initialize SingleStore client lazily."""
|
|
66
|
+
if self._initialized:
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
import singlestoredb as s2
|
|
71
|
+
except ImportError:
|
|
72
|
+
raise ImportError(
|
|
73
|
+
"singlestoredb is required for SingleStore Vector support. "
|
|
74
|
+
"Install with: pip install singlestoredb"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if self.url:
|
|
78
|
+
self._conn = s2.connect(self.url)
|
|
79
|
+
else:
|
|
80
|
+
self._conn = s2.connect(
|
|
81
|
+
host=self.host,
|
|
82
|
+
port=self.port,
|
|
83
|
+
database=self.database,
|
|
84
|
+
user=self.user,
|
|
85
|
+
password=self.password
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
self._initialized = True
|
|
89
|
+
|
|
90
|
+
def create_collection(
|
|
91
|
+
self,
|
|
92
|
+
name: str,
|
|
93
|
+
dimension: int = 1536,
|
|
94
|
+
distance: str = "cosine",
|
|
95
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
96
|
+
) -> None:
|
|
97
|
+
"""Create a vector table."""
|
|
98
|
+
self._init_client()
|
|
99
|
+
|
|
100
|
+
table_name = f"{self.table_prefix}{name}"
|
|
101
|
+
|
|
102
|
+
with self._conn.cursor() as cur:
|
|
103
|
+
cur.execute(f"""
|
|
104
|
+
CREATE TABLE IF NOT EXISTS {table_name} (
|
|
105
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
106
|
+
content TEXT,
|
|
107
|
+
embedding VECTOR({dimension}),
|
|
108
|
+
metadata JSON,
|
|
109
|
+
content_hash VARCHAR(64),
|
|
110
|
+
created_at DOUBLE
|
|
111
|
+
)
|
|
112
|
+
""")
|
|
113
|
+
|
|
114
|
+
def delete_collection(self, name: str) -> bool:
|
|
115
|
+
"""Delete a vector table."""
|
|
116
|
+
self._init_client()
|
|
117
|
+
|
|
118
|
+
table_name = f"{self.table_prefix}{name}"
|
|
119
|
+
try:
|
|
120
|
+
with self._conn.cursor() as cur:
|
|
121
|
+
cur.execute(f"DROP TABLE IF EXISTS {table_name}")
|
|
122
|
+
return True
|
|
123
|
+
except Exception as e:
|
|
124
|
+
logger.error(f"Failed to delete collection {name}: {e}")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def collection_exists(self, name: str) -> bool:
|
|
128
|
+
"""Check if table exists."""
|
|
129
|
+
self._init_client()
|
|
130
|
+
|
|
131
|
+
table_name = f"{self.table_prefix}{name}"
|
|
132
|
+
with self._conn.cursor() as cur:
|
|
133
|
+
cur.execute(f"""
|
|
134
|
+
SELECT COUNT(*) FROM information_schema.tables
|
|
135
|
+
WHERE table_schema = %s AND table_name = %s
|
|
136
|
+
""", (self.database, table_name))
|
|
137
|
+
return cur.fetchone()[0] > 0
|
|
138
|
+
|
|
139
|
+
def list_collections(self) -> List[str]:
|
|
140
|
+
"""List all vector tables."""
|
|
141
|
+
self._init_client()
|
|
142
|
+
|
|
143
|
+
with self._conn.cursor() as cur:
|
|
144
|
+
cur.execute(f"""
|
|
145
|
+
SELECT table_name FROM information_schema.tables
|
|
146
|
+
WHERE table_schema = %s AND table_name LIKE %s
|
|
147
|
+
""", (self.database, f"{self.table_prefix}%"))
|
|
148
|
+
return [row[0][len(self.table_prefix):] for row in cur.fetchall()]
|
|
149
|
+
|
|
150
|
+
def insert(
|
|
151
|
+
self,
|
|
152
|
+
collection: str,
|
|
153
|
+
documents: List[KnowledgeDocument]
|
|
154
|
+
) -> List[str]:
|
|
155
|
+
"""Insert documents."""
|
|
156
|
+
self._init_client()
|
|
157
|
+
|
|
158
|
+
table_name = f"{self.table_prefix}{collection}"
|
|
159
|
+
ids = []
|
|
160
|
+
|
|
161
|
+
with self._conn.cursor() as cur:
|
|
162
|
+
for doc in documents:
|
|
163
|
+
embedding_str = str(doc.embedding) if doc.embedding else None
|
|
164
|
+
cur.execute(f"""
|
|
165
|
+
INSERT INTO {table_name} (id, content, embedding, metadata, content_hash, created_at)
|
|
166
|
+
VALUES (%s, %s, %s, %s, %s, %s)
|
|
167
|
+
""", (doc.id, doc.content, embedding_str,
|
|
168
|
+
str(doc.metadata) if doc.metadata else None,
|
|
169
|
+
doc.content_hash, doc.created_at))
|
|
170
|
+
ids.append(doc.id)
|
|
171
|
+
|
|
172
|
+
return ids
|
|
173
|
+
|
|
174
|
+
def upsert(
|
|
175
|
+
self,
|
|
176
|
+
collection: str,
|
|
177
|
+
documents: List[KnowledgeDocument]
|
|
178
|
+
) -> List[str]:
|
|
179
|
+
"""Upsert documents."""
|
|
180
|
+
self._init_client()
|
|
181
|
+
|
|
182
|
+
table_name = f"{self.table_prefix}{collection}"
|
|
183
|
+
ids = []
|
|
184
|
+
|
|
185
|
+
with self._conn.cursor() as cur:
|
|
186
|
+
for doc in documents:
|
|
187
|
+
embedding_str = str(doc.embedding) if doc.embedding else None
|
|
188
|
+
cur.execute(f"""
|
|
189
|
+
REPLACE INTO {table_name} (id, content, embedding, metadata, content_hash, created_at)
|
|
190
|
+
VALUES (%s, %s, %s, %s, %s, %s)
|
|
191
|
+
""", (doc.id, doc.content, embedding_str,
|
|
192
|
+
str(doc.metadata) if doc.metadata else None,
|
|
193
|
+
doc.content_hash, doc.created_at))
|
|
194
|
+
ids.append(doc.id)
|
|
195
|
+
|
|
196
|
+
return ids
|
|
197
|
+
|
|
198
|
+
def search(
|
|
199
|
+
self,
|
|
200
|
+
collection: str,
|
|
201
|
+
query_embedding: List[float],
|
|
202
|
+
limit: int = 5,
|
|
203
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
204
|
+
score_threshold: Optional[float] = None
|
|
205
|
+
) -> List[KnowledgeDocument]:
|
|
206
|
+
"""Search for similar documents using vector search."""
|
|
207
|
+
self._init_client()
|
|
208
|
+
|
|
209
|
+
table_name = f"{self.table_prefix}{collection}"
|
|
210
|
+
embedding_str = str(query_embedding)
|
|
211
|
+
|
|
212
|
+
with self._conn.cursor() as cur:
|
|
213
|
+
cur.execute(f"""
|
|
214
|
+
SELECT id, content, metadata, content_hash, created_at,
|
|
215
|
+
DOT_PRODUCT(embedding, %s) as score
|
|
216
|
+
FROM {table_name}
|
|
217
|
+
ORDER BY score DESC
|
|
218
|
+
LIMIT %s
|
|
219
|
+
""", (embedding_str, limit))
|
|
220
|
+
|
|
221
|
+
documents = []
|
|
222
|
+
for row in cur.fetchall():
|
|
223
|
+
score = row[5] if len(row) > 5 else 0
|
|
224
|
+
if score_threshold and score < score_threshold:
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
documents.append(KnowledgeDocument(
|
|
228
|
+
id=row[0],
|
|
229
|
+
content=row[1],
|
|
230
|
+
metadata=eval(row[2]) if row[2] else None,
|
|
231
|
+
content_hash=row[3],
|
|
232
|
+
created_at=row[4]
|
|
233
|
+
))
|
|
234
|
+
|
|
235
|
+
return documents
|
|
236
|
+
|
|
237
|
+
def get(
|
|
238
|
+
self,
|
|
239
|
+
collection: str,
|
|
240
|
+
ids: List[str]
|
|
241
|
+
) -> List[KnowledgeDocument]:
|
|
242
|
+
"""Get documents by IDs."""
|
|
243
|
+
self._init_client()
|
|
244
|
+
|
|
245
|
+
table_name = f"{self.table_prefix}{collection}"
|
|
246
|
+
placeholders = ','.join(['%s'] * len(ids))
|
|
247
|
+
|
|
248
|
+
with self._conn.cursor() as cur:
|
|
249
|
+
cur.execute(f"""
|
|
250
|
+
SELECT id, content, metadata, content_hash, created_at
|
|
251
|
+
FROM {table_name}
|
|
252
|
+
WHERE id IN ({placeholders})
|
|
253
|
+
""", ids)
|
|
254
|
+
|
|
255
|
+
return [
|
|
256
|
+
KnowledgeDocument(
|
|
257
|
+
id=row[0],
|
|
258
|
+
content=row[1],
|
|
259
|
+
metadata=eval(row[2]) if row[2] else None,
|
|
260
|
+
content_hash=row[3],
|
|
261
|
+
created_at=row[4]
|
|
262
|
+
)
|
|
263
|
+
for row in cur.fetchall()
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
def delete(
|
|
267
|
+
self,
|
|
268
|
+
collection: str,
|
|
269
|
+
ids: Optional[List[str]] = None,
|
|
270
|
+
filters: Optional[Dict[str, Any]] = None
|
|
271
|
+
) -> int:
|
|
272
|
+
"""Delete documents."""
|
|
273
|
+
self._init_client()
|
|
274
|
+
|
|
275
|
+
table_name = f"{self.table_prefix}{collection}"
|
|
276
|
+
|
|
277
|
+
with self._conn.cursor() as cur:
|
|
278
|
+
if ids:
|
|
279
|
+
placeholders = ','.join(['%s'] * len(ids))
|
|
280
|
+
cur.execute(f"DELETE FROM {table_name} WHERE id IN ({placeholders})", ids)
|
|
281
|
+
else:
|
|
282
|
+
cur.execute(f"DELETE FROM {table_name}")
|
|
283
|
+
return cur.rowcount
|
|
284
|
+
|
|
285
|
+
def count(self, collection: str) -> int:
|
|
286
|
+
"""Count documents."""
|
|
287
|
+
self._init_client()
|
|
288
|
+
|
|
289
|
+
table_name = f"{self.table_prefix}{collection}"
|
|
290
|
+
with self._conn.cursor() as cur:
|
|
291
|
+
cur.execute(f"SELECT COUNT(*) FROM {table_name}")
|
|
292
|
+
return cur.fetchone()[0]
|
|
293
|
+
|
|
294
|
+
def close(self) -> None:
|
|
295
|
+
"""Close the connection."""
|
|
296
|
+
if self._conn:
|
|
297
|
+
self._conn.close()
|
|
298
|
+
self._conn = None
|
|
299
|
+
self._initialized = False
|