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,471 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@ Mention Autocomplete for PraisonAI CLI.
|
|
3
|
+
|
|
4
|
+
Provides file/directory autocomplete when user types @.
|
|
5
|
+
Uses only stdlib imports for zero performance impact.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Detect @ trigger in input
|
|
9
|
+
- Fuzzy search files/directories
|
|
10
|
+
- Cache results for performance
|
|
11
|
+
- Integrate with prompt_toolkit
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import time
|
|
16
|
+
import fnmatch
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import List, Optional, Dict, Tuple
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ============================================================================
|
|
22
|
+
# Data Classes
|
|
23
|
+
# ============================================================================
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class AtMentionContext:
|
|
27
|
+
"""Context for an @ mention in user input."""
|
|
28
|
+
is_active: bool
|
|
29
|
+
query: str
|
|
30
|
+
start_pos: int
|
|
31
|
+
mention_type: str = "file" # file, directory, web, etc.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class FileSuggestion:
|
|
36
|
+
"""A file or directory suggestion."""
|
|
37
|
+
path: str
|
|
38
|
+
file_type: str # "file" or "directory"
|
|
39
|
+
score: int = 100
|
|
40
|
+
display: str = ""
|
|
41
|
+
|
|
42
|
+
def __post_init__(self):
|
|
43
|
+
if not self.display:
|
|
44
|
+
self.display = self.path
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ============================================================================
|
|
48
|
+
# @ Mention Detection
|
|
49
|
+
# ============================================================================
|
|
50
|
+
|
|
51
|
+
def detect_at_mention(text: str, cursor_pos: int) -> Optional[AtMentionContext]:
|
|
52
|
+
"""
|
|
53
|
+
Detect if cursor is within an @ mention context.
|
|
54
|
+
|
|
55
|
+
Scans backward from cursor to find @ symbol.
|
|
56
|
+
Returns None if not in @ context.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
text: Full input text
|
|
60
|
+
cursor_pos: Current cursor position
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
AtMentionContext if in @ mention, None otherwise
|
|
64
|
+
"""
|
|
65
|
+
if cursor_pos <= 0 or cursor_pos > len(text):
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
# Scan backward from cursor to find @
|
|
69
|
+
at_pos = -1
|
|
70
|
+
for i in range(cursor_pos - 1, -1, -1):
|
|
71
|
+
char = text[i]
|
|
72
|
+
|
|
73
|
+
# Found @
|
|
74
|
+
if char == '@':
|
|
75
|
+
at_pos = i
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
# Space/newline breaks @ context
|
|
79
|
+
if char in ' \t\n\r':
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
if at_pos == -1:
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
# Extract query between @ and cursor
|
|
86
|
+
query = text[at_pos + 1:cursor_pos]
|
|
87
|
+
|
|
88
|
+
return AtMentionContext(
|
|
89
|
+
is_active=True,
|
|
90
|
+
query=query,
|
|
91
|
+
start_pos=at_pos
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ============================================================================
|
|
96
|
+
# File Search Service
|
|
97
|
+
# ============================================================================
|
|
98
|
+
|
|
99
|
+
class FileSearchService:
|
|
100
|
+
"""
|
|
101
|
+
Service for searching files in a directory.
|
|
102
|
+
|
|
103
|
+
Features:
|
|
104
|
+
- Walk directory tree
|
|
105
|
+
- Fuzzy match file paths
|
|
106
|
+
- Cache results with TTL
|
|
107
|
+
- Respect .gitignore (basic)
|
|
108
|
+
- Support absolute paths (@/Users/...) and home paths (@~/...)
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(
|
|
112
|
+
self,
|
|
113
|
+
root_dir: str,
|
|
114
|
+
cache_ttl: int = 30,
|
|
115
|
+
max_depth: int = 5,
|
|
116
|
+
ignore_patterns: Optional[List[str]] = None
|
|
117
|
+
):
|
|
118
|
+
self._root = os.path.abspath(root_dir)
|
|
119
|
+
self._cache_ttl = cache_ttl
|
|
120
|
+
self._max_depth = max_depth
|
|
121
|
+
self._ignore_patterns = ignore_patterns or [
|
|
122
|
+
'.git', '__pycache__', 'node_modules', '.venv', 'venv',
|
|
123
|
+
'*.pyc', '*.pyo', '.DS_Store', '*.egg-info'
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
# Cache: query -> (results, timestamp)
|
|
127
|
+
self._cache: Dict[str, Tuple[List[FileSuggestion], float]] = {}
|
|
128
|
+
|
|
129
|
+
# File list cache per directory
|
|
130
|
+
self._file_list: Optional[List[str]] = None
|
|
131
|
+
self._file_list_time: float = 0
|
|
132
|
+
self._file_list_dir: Optional[str] = None
|
|
133
|
+
|
|
134
|
+
def _should_ignore(self, name: str) -> bool:
|
|
135
|
+
"""Check if file/dir should be ignored."""
|
|
136
|
+
for pattern in self._ignore_patterns:
|
|
137
|
+
if fnmatch.fnmatch(name, pattern):
|
|
138
|
+
return True
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
def _get_file_list(self, search_dir: str = None) -> List[str]:
|
|
142
|
+
"""Get list of all files (cached)."""
|
|
143
|
+
now = time.time()
|
|
144
|
+
target_dir = search_dir or self._root
|
|
145
|
+
|
|
146
|
+
# Return cached if fresh and same directory
|
|
147
|
+
if (self._file_list is not None and
|
|
148
|
+
self._file_list_dir == target_dir and
|
|
149
|
+
(now - self._file_list_time) < self._cache_ttl):
|
|
150
|
+
return self._file_list
|
|
151
|
+
|
|
152
|
+
files = []
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
for root, dirs, filenames in os.walk(target_dir):
|
|
156
|
+
# Calculate depth
|
|
157
|
+
rel_root = os.path.relpath(root, target_dir)
|
|
158
|
+
depth = 0 if rel_root == '.' else rel_root.count(os.sep) + 1
|
|
159
|
+
|
|
160
|
+
if depth > self._max_depth:
|
|
161
|
+
dirs.clear() # Don't descend further
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
# Filter ignored directories
|
|
165
|
+
dirs[:] = [d for d in dirs if not self._should_ignore(d)]
|
|
166
|
+
|
|
167
|
+
# Add directories
|
|
168
|
+
for d in dirs:
|
|
169
|
+
rel_path = os.path.relpath(os.path.join(root, d), target_dir)
|
|
170
|
+
files.append(rel_path + os.sep)
|
|
171
|
+
|
|
172
|
+
# Add files
|
|
173
|
+
for f in filenames:
|
|
174
|
+
if not self._should_ignore(f):
|
|
175
|
+
rel_path = os.path.relpath(os.path.join(root, f), target_dir)
|
|
176
|
+
files.append(rel_path)
|
|
177
|
+
except OSError:
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
self._file_list = files
|
|
181
|
+
self._file_list_time = now
|
|
182
|
+
self._file_list_dir = target_dir
|
|
183
|
+
return files
|
|
184
|
+
|
|
185
|
+
def _fuzzy_match(self, text: str, query: str) -> int:
|
|
186
|
+
"""
|
|
187
|
+
Simple fuzzy match scoring.
|
|
188
|
+
|
|
189
|
+
Returns score 0-100, higher is better.
|
|
190
|
+
"""
|
|
191
|
+
if not query:
|
|
192
|
+
return 50 # Default score for empty query
|
|
193
|
+
|
|
194
|
+
text_lower = text.lower()
|
|
195
|
+
query_lower = query.lower()
|
|
196
|
+
|
|
197
|
+
# Exact match
|
|
198
|
+
if text_lower == query_lower:
|
|
199
|
+
return 100
|
|
200
|
+
|
|
201
|
+
# Starts with
|
|
202
|
+
if text_lower.startswith(query_lower):
|
|
203
|
+
return 90
|
|
204
|
+
|
|
205
|
+
# Contains
|
|
206
|
+
if query_lower in text_lower:
|
|
207
|
+
return 80
|
|
208
|
+
|
|
209
|
+
# Fuzzy: all query chars appear in order
|
|
210
|
+
text_idx = 0
|
|
211
|
+
query_idx = 0
|
|
212
|
+
matches = 0
|
|
213
|
+
|
|
214
|
+
while text_idx < len(text_lower) and query_idx < len(query_lower):
|
|
215
|
+
if text_lower[text_idx] == query_lower[query_idx]:
|
|
216
|
+
matches += 1
|
|
217
|
+
query_idx += 1
|
|
218
|
+
text_idx += 1
|
|
219
|
+
|
|
220
|
+
if query_idx == len(query_lower):
|
|
221
|
+
# All query chars found
|
|
222
|
+
return 50 + int(30 * matches / len(text))
|
|
223
|
+
|
|
224
|
+
return 0
|
|
225
|
+
|
|
226
|
+
def search(self, query: str, max_results: int = 20) -> List[FileSuggestion]:
|
|
227
|
+
"""
|
|
228
|
+
Search for files matching query.
|
|
229
|
+
|
|
230
|
+
Supports:
|
|
231
|
+
- Relative paths: @file.txt, @src/main.py
|
|
232
|
+
- Home paths: @~/Documents, @~/.config
|
|
233
|
+
- Absolute paths: @/Users/name/file.txt, @/etc/hosts
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
query: Search query (fuzzy matched)
|
|
237
|
+
max_results: Maximum results to return
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
List of FileSuggestion sorted by score
|
|
241
|
+
"""
|
|
242
|
+
# Determine search directory based on query
|
|
243
|
+
search_dir = self._root
|
|
244
|
+
search_query = query
|
|
245
|
+
prefix = ""
|
|
246
|
+
|
|
247
|
+
# Handle home directory paths (@~/)
|
|
248
|
+
if query.startswith('~'):
|
|
249
|
+
expanded = os.path.expanduser(query)
|
|
250
|
+
# Find the directory part and filename part
|
|
251
|
+
if os.path.isdir(expanded):
|
|
252
|
+
search_dir = expanded
|
|
253
|
+
search_query = ""
|
|
254
|
+
prefix = query.rstrip('/') + '/'
|
|
255
|
+
else:
|
|
256
|
+
parent = os.path.dirname(expanded)
|
|
257
|
+
if os.path.isdir(parent):
|
|
258
|
+
search_dir = parent
|
|
259
|
+
search_query = os.path.basename(expanded)
|
|
260
|
+
prefix = os.path.dirname(query).rstrip('/') + '/'
|
|
261
|
+
else:
|
|
262
|
+
# Just expand ~ and search from home
|
|
263
|
+
search_dir = os.path.expanduser('~')
|
|
264
|
+
search_query = query[2:] if query.startswith('~/') else query[1:]
|
|
265
|
+
prefix = "~/"
|
|
266
|
+
|
|
267
|
+
# Handle absolute paths (@/Users/..., @/etc/...)
|
|
268
|
+
elif query.startswith('/'):
|
|
269
|
+
if os.path.isdir(query):
|
|
270
|
+
search_dir = query
|
|
271
|
+
search_query = ""
|
|
272
|
+
prefix = query.rstrip('/') + '/'
|
|
273
|
+
else:
|
|
274
|
+
parent = os.path.dirname(query)
|
|
275
|
+
if os.path.isdir(parent):
|
|
276
|
+
search_dir = parent
|
|
277
|
+
search_query = os.path.basename(query)
|
|
278
|
+
prefix = parent.rstrip('/') + '/'
|
|
279
|
+
else:
|
|
280
|
+
# Search from root with the query
|
|
281
|
+
search_dir = '/'
|
|
282
|
+
search_query = query[1:]
|
|
283
|
+
prefix = "/"
|
|
284
|
+
|
|
285
|
+
# Check cache
|
|
286
|
+
cache_key = f"{search_dir}:{search_query}:{max_results}"
|
|
287
|
+
now = time.time()
|
|
288
|
+
|
|
289
|
+
if cache_key in self._cache:
|
|
290
|
+
results, cached_time = self._cache[cache_key]
|
|
291
|
+
if (now - cached_time) < self._cache_ttl:
|
|
292
|
+
return results
|
|
293
|
+
|
|
294
|
+
# Get all files from the appropriate directory
|
|
295
|
+
all_files = self._get_file_list(search_dir)
|
|
296
|
+
|
|
297
|
+
# Score and filter
|
|
298
|
+
scored: List[Tuple[int, str]] = []
|
|
299
|
+
for path in all_files:
|
|
300
|
+
score = self._fuzzy_match(path, search_query)
|
|
301
|
+
if score > 0:
|
|
302
|
+
scored.append((score, path))
|
|
303
|
+
|
|
304
|
+
# Sort by score descending
|
|
305
|
+
scored.sort(key=lambda x: (-x[0], x[1]))
|
|
306
|
+
|
|
307
|
+
# Build results with proper prefix
|
|
308
|
+
results = []
|
|
309
|
+
for score, path in scored[:max_results]:
|
|
310
|
+
file_type = "directory" if path.endswith(os.sep) else "file"
|
|
311
|
+
display_path = prefix + path if prefix else path
|
|
312
|
+
results.append(FileSuggestion(
|
|
313
|
+
path=display_path,
|
|
314
|
+
file_type=file_type,
|
|
315
|
+
score=score
|
|
316
|
+
))
|
|
317
|
+
|
|
318
|
+
# Cache results
|
|
319
|
+
self._cache[cache_key] = (results, now)
|
|
320
|
+
|
|
321
|
+
return results
|
|
322
|
+
|
|
323
|
+
def clear_cache(self) -> None:
|
|
324
|
+
"""Clear all caches."""
|
|
325
|
+
self._cache.clear()
|
|
326
|
+
self._file_list = None
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# ============================================================================
|
|
330
|
+
# Completers for prompt_toolkit
|
|
331
|
+
# ============================================================================
|
|
332
|
+
|
|
333
|
+
# Lazy import to avoid performance impact
|
|
334
|
+
_Completer = None
|
|
335
|
+
_Completion = None
|
|
336
|
+
|
|
337
|
+
def _get_prompt_toolkit():
|
|
338
|
+
"""Lazy import prompt_toolkit."""
|
|
339
|
+
global _Completer, _Completion
|
|
340
|
+
if _Completer is None:
|
|
341
|
+
from prompt_toolkit.completion import Completer, Completion
|
|
342
|
+
_Completer = Completer
|
|
343
|
+
_Completion = Completion
|
|
344
|
+
return _Completer, _Completion
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class AtMentionCompleter:
|
|
348
|
+
"""
|
|
349
|
+
Completer for @ mentions.
|
|
350
|
+
|
|
351
|
+
Shows file suggestions when user types @.
|
|
352
|
+
Implements prompt_toolkit Completer interface.
|
|
353
|
+
"""
|
|
354
|
+
|
|
355
|
+
def __init__(self, root_dir: str):
|
|
356
|
+
self._file_service = FileSearchService(root_dir)
|
|
357
|
+
|
|
358
|
+
def get_completions(self, document, complete_event):
|
|
359
|
+
"""Get completions for @ mentions."""
|
|
360
|
+
_, Completion = _get_prompt_toolkit()
|
|
361
|
+
|
|
362
|
+
text = document.text_before_cursor
|
|
363
|
+
cursor_pos = len(text)
|
|
364
|
+
|
|
365
|
+
# Detect @ context
|
|
366
|
+
context = detect_at_mention(text, cursor_pos)
|
|
367
|
+
if context is None or not context.is_active:
|
|
368
|
+
return
|
|
369
|
+
|
|
370
|
+
# Search files
|
|
371
|
+
results = self._file_service.search(context.query, max_results=15)
|
|
372
|
+
|
|
373
|
+
# Yield completions
|
|
374
|
+
for suggestion in results:
|
|
375
|
+
# Icon based on type
|
|
376
|
+
icon = "📁 " if suggestion.file_type == "directory" else "📄 "
|
|
377
|
+
display = f"{icon}{suggestion.path}"
|
|
378
|
+
|
|
379
|
+
# Calculate replacement position
|
|
380
|
+
# Replace from @ to cursor
|
|
381
|
+
start_position = -(cursor_pos - context.start_pos)
|
|
382
|
+
|
|
383
|
+
yield Completion(
|
|
384
|
+
text=f"@{suggestion.path}",
|
|
385
|
+
start_position=start_position,
|
|
386
|
+
display=display
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def create_combined_completer(commands: List[str], root_dir: str):
|
|
391
|
+
"""
|
|
392
|
+
Factory function to create a CombinedCompleter.
|
|
393
|
+
|
|
394
|
+
Returns a proper Completer subclass that handles both / and @.
|
|
395
|
+
"""
|
|
396
|
+
Completer, Completion = _get_prompt_toolkit()
|
|
397
|
+
file_service = FileSearchService(root_dir)
|
|
398
|
+
|
|
399
|
+
class CombinedCompleter(Completer):
|
|
400
|
+
"""
|
|
401
|
+
Combined completer for both / commands and @ mentions.
|
|
402
|
+
|
|
403
|
+
Properly inherits from prompt_toolkit Completer.
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
def get_completions(self, document, complete_event):
|
|
407
|
+
"""Get completions for / or @."""
|
|
408
|
+
text = document.text_before_cursor.lstrip()
|
|
409
|
+
|
|
410
|
+
# Check for slash command
|
|
411
|
+
if text.startswith('/'):
|
|
412
|
+
cmd_text = text[1:].lower()
|
|
413
|
+
for cmd in commands:
|
|
414
|
+
if cmd.lower().startswith(cmd_text):
|
|
415
|
+
yield Completion(
|
|
416
|
+
f'/{cmd}',
|
|
417
|
+
start_position=-len(text),
|
|
418
|
+
display=f'/{cmd}'
|
|
419
|
+
)
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
# Check for @ mention
|
|
423
|
+
full_text = document.text_before_cursor
|
|
424
|
+
cursor_pos = len(full_text)
|
|
425
|
+
|
|
426
|
+
context = detect_at_mention(full_text, cursor_pos)
|
|
427
|
+
if context is None or not context.is_active:
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
# Search files
|
|
431
|
+
results = file_service.search(context.query, max_results=15)
|
|
432
|
+
|
|
433
|
+
# Yield completions
|
|
434
|
+
for suggestion in results:
|
|
435
|
+
icon = "📁 " if suggestion.file_type == "directory" else "📄 "
|
|
436
|
+
display = f"{icon}{suggestion.path}"
|
|
437
|
+
start_position = -(cursor_pos - context.start_pos)
|
|
438
|
+
|
|
439
|
+
yield Completion(
|
|
440
|
+
text=f"@{suggestion.path}",
|
|
441
|
+
start_position=start_position,
|
|
442
|
+
display=display
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
return CombinedCompleter()
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
# Backward compatible alias
|
|
449
|
+
class CombinedCompleter:
|
|
450
|
+
"""
|
|
451
|
+
Wrapper class for backward compatibility.
|
|
452
|
+
|
|
453
|
+
Use create_combined_completer() for proper Completer subclass.
|
|
454
|
+
"""
|
|
455
|
+
|
|
456
|
+
def __new__(cls, commands: List[str], root_dir: str):
|
|
457
|
+
return create_combined_completer(commands, root_dir)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
# ============================================================================
|
|
461
|
+
# Exports
|
|
462
|
+
# ============================================================================
|
|
463
|
+
|
|
464
|
+
__all__ = [
|
|
465
|
+
'AtMentionContext',
|
|
466
|
+
'FileSuggestion',
|
|
467
|
+
'detect_at_mention',
|
|
468
|
+
'FileSearchService',
|
|
469
|
+
'AtMentionCompleter',
|
|
470
|
+
'CombinedCompleter',
|
|
471
|
+
]
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto Memory Handler for CLI.
|
|
3
|
+
|
|
4
|
+
Provides automatic memory extraction and storage.
|
|
5
|
+
Usage: praisonai "Learn about user preferences" --auto-memory
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Tuple
|
|
9
|
+
from .base import FlagHandler
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AutoMemoryHandler(FlagHandler):
|
|
13
|
+
"""
|
|
14
|
+
Handler for --auto-memory flag.
|
|
15
|
+
|
|
16
|
+
Automatically extracts and stores important information from conversations.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
praisonai "Learn about user preferences" --auto-memory
|
|
20
|
+
praisonai "Remember this context" --auto-memory --user-id myuser
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def feature_name(self) -> str:
|
|
25
|
+
return "auto_memory"
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def flag_name(self) -> str:
|
|
29
|
+
return "auto-memory"
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def flag_help(self) -> str:
|
|
33
|
+
return "Enable automatic memory extraction and storage"
|
|
34
|
+
|
|
35
|
+
def check_dependencies(self) -> Tuple[bool, str]:
|
|
36
|
+
"""Check if AutoMemory is available."""
|
|
37
|
+
try:
|
|
38
|
+
import importlib.util
|
|
39
|
+
if importlib.util.find_spec("praisonaiagents") is not None:
|
|
40
|
+
return True, ""
|
|
41
|
+
return False, "praisonaiagents not installed"
|
|
42
|
+
except ImportError:
|
|
43
|
+
return False, "praisonaiagents not installed. Install with: pip install praisonaiagents"
|
|
44
|
+
|
|
45
|
+
def _get_auto_memory(self, user_id: str = None):
|
|
46
|
+
"""Get AutoMemory instance lazily."""
|
|
47
|
+
try:
|
|
48
|
+
from praisonaiagents.memory import AutoMemory, FileMemory
|
|
49
|
+
# AutoMemory requires a FileMemory instance
|
|
50
|
+
memory = FileMemory(user_id=user_id or "default")
|
|
51
|
+
return AutoMemory(memory=memory)
|
|
52
|
+
except ImportError:
|
|
53
|
+
self.print_status(
|
|
54
|
+
"AutoMemory requires praisonaiagents. Install with: pip install praisonaiagents",
|
|
55
|
+
"error"
|
|
56
|
+
)
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
def apply_to_agent_config(self, config: Dict[str, Any], flag_value: Any) -> Dict[str, Any]:
|
|
60
|
+
"""
|
|
61
|
+
Apply auto memory configuration.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
config: Agent configuration dictionary
|
|
65
|
+
flag_value: Boolean or dict with user_id
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Modified configuration
|
|
69
|
+
"""
|
|
70
|
+
if flag_value:
|
|
71
|
+
config['auto_memory'] = True
|
|
72
|
+
if isinstance(flag_value, dict):
|
|
73
|
+
config['auto_memory_user_id'] = flag_value.get('user_id', 'default')
|
|
74
|
+
return config
|
|
75
|
+
|
|
76
|
+
def extract_memories(self, text: str, user_id: str = None, user_message: str = None) -> list:
|
|
77
|
+
"""
|
|
78
|
+
Extract and store memories from text.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
text: Assistant response text to extract memories from
|
|
82
|
+
user_id: User ID for memory isolation
|
|
83
|
+
user_message: Original user message (for context)
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of extracted memories
|
|
87
|
+
"""
|
|
88
|
+
auto_memory = self._get_auto_memory(user_id)
|
|
89
|
+
if not auto_memory:
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# Use process_interaction which extracts AND stores memories
|
|
94
|
+
memories = auto_memory.process_interaction(
|
|
95
|
+
user_message=user_message or text,
|
|
96
|
+
assistant_response=text if user_message else None,
|
|
97
|
+
store=True # Store the memories
|
|
98
|
+
)
|
|
99
|
+
if memories:
|
|
100
|
+
self.print_status(f"🧠 Extracted and stored {len(memories)} memories", "success")
|
|
101
|
+
return memories
|
|
102
|
+
except Exception as e:
|
|
103
|
+
self.log(f"Memory extraction failed: {e}", "error")
|
|
104
|
+
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
def store_memory(self, content: str, user_id: str = None, importance: float = 0.5) -> bool:
|
|
108
|
+
"""
|
|
109
|
+
Store a memory.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
content: Memory content
|
|
113
|
+
user_id: User ID for memory isolation
|
|
114
|
+
importance: Importance score (0-1)
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
True if successful
|
|
118
|
+
"""
|
|
119
|
+
auto_memory = self._get_auto_memory(user_id)
|
|
120
|
+
if not auto_memory:
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
if hasattr(auto_memory, 'store'):
|
|
125
|
+
auto_memory.store(content, importance=importance)
|
|
126
|
+
elif hasattr(auto_memory, 'add'):
|
|
127
|
+
auto_memory.add(content, importance=importance)
|
|
128
|
+
|
|
129
|
+
self.print_status("✅ Memory stored", "success")
|
|
130
|
+
return True
|
|
131
|
+
except Exception as e:
|
|
132
|
+
self.log(f"Memory storage failed: {e}", "error")
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def post_process_result(self, result: Any, flag_value: Any, user_message: str = None) -> Any:
|
|
136
|
+
"""
|
|
137
|
+
Post-process result to extract and store memories.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
result: Agent output
|
|
141
|
+
flag_value: Boolean or dict with configuration
|
|
142
|
+
user_message: Original user message for context
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Original result (memories are stored)
|
|
146
|
+
"""
|
|
147
|
+
if not flag_value:
|
|
148
|
+
return result
|
|
149
|
+
|
|
150
|
+
user_id = None
|
|
151
|
+
if isinstance(flag_value, dict):
|
|
152
|
+
user_id = flag_value.get('user_id')
|
|
153
|
+
user_message = flag_value.get('user_message', user_message)
|
|
154
|
+
|
|
155
|
+
# Extract and store memories from result
|
|
156
|
+
text = str(result)
|
|
157
|
+
memories = self.extract_memories(text, user_id, user_message=user_message)
|
|
158
|
+
|
|
159
|
+
if memories:
|
|
160
|
+
self.print_status("\n🧠 Auto-extracted Memories:", "info")
|
|
161
|
+
for mem in memories:
|
|
162
|
+
mem_type = mem.get('type', 'unknown')
|
|
163
|
+
content = mem.get('content', str(mem))[:50]
|
|
164
|
+
self.print_status(f" • {mem_type}: {content}...", "info")
|
|
165
|
+
|
|
166
|
+
return result
|
|
167
|
+
|
|
168
|
+
def execute(self, text: str = None, user_id: str = None, **kwargs) -> Dict[str, Any]:
|
|
169
|
+
"""
|
|
170
|
+
Execute auto memory extraction.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
text: Text to process
|
|
174
|
+
user_id: User ID for memory isolation
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Dictionary of extracted memories
|
|
178
|
+
"""
|
|
179
|
+
if not text:
|
|
180
|
+
return {}
|
|
181
|
+
|
|
182
|
+
return self.extract_memories(text, user_id)
|