agent_os_kernel 3.1.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.
- agent_control_plane/__init__.py +662 -0
- agent_control_plane/a2a_adapter.py +543 -0
- agent_control_plane/adapter.py +417 -0
- agent_control_plane/agent_hibernation.py +394 -0
- agent_control_plane/agent_kernel.py +470 -0
- agent_control_plane/compliance.py +720 -0
- agent_control_plane/constraint_graphs.py +478 -0
- agent_control_plane/control_plane.py +854 -0
- agent_control_plane/example_executors.py +195 -0
- agent_control_plane/execution_engine.py +231 -0
- agent_control_plane/flight_recorder.py +846 -0
- agent_control_plane/governance_layer.py +435 -0
- agent_control_plane/hf_utils.py +563 -0
- agent_control_plane/interfaces/__init__.py +55 -0
- agent_control_plane/interfaces/kernel_interface.py +361 -0
- agent_control_plane/interfaces/plugin_interface.py +497 -0
- agent_control_plane/interfaces/protocol_interfaces.py +387 -0
- agent_control_plane/kernel_space.py +1009 -0
- agent_control_plane/langchain_adapter.py +424 -0
- agent_control_plane/lifecycle.py +3113 -0
- agent_control_plane/mcp_adapter.py +653 -0
- agent_control_plane/ml_safety.py +563 -0
- agent_control_plane/multimodal.py +727 -0
- agent_control_plane/mute_agent.py +422 -0
- agent_control_plane/observability.py +787 -0
- agent_control_plane/orchestrator.py +482 -0
- agent_control_plane/plugin_registry.py +750 -0
- agent_control_plane/policy_engine.py +954 -0
- agent_control_plane/process_isolation.py +777 -0
- agent_control_plane/shadow_mode.py +310 -0
- agent_control_plane/signals.py +493 -0
- agent_control_plane/supervisor_agents.py +430 -0
- agent_control_plane/time_travel_debugger.py +557 -0
- agent_control_plane/tool_registry.py +452 -0
- agent_control_plane/vfs.py +697 -0
- agent_kernel/__init__.py +69 -0
- agent_kernel/analyzer.py +435 -0
- agent_kernel/auditor.py +36 -0
- agent_kernel/completeness_auditor.py +237 -0
- agent_kernel/detector.py +203 -0
- agent_kernel/kernel.py +744 -0
- agent_kernel/memory_manager.py +85 -0
- agent_kernel/models.py +374 -0
- agent_kernel/nudge_mechanism.py +263 -0
- agent_kernel/outcome_analyzer.py +338 -0
- agent_kernel/patcher.py +582 -0
- agent_kernel/semantic_analyzer.py +316 -0
- agent_kernel/semantic_purge.py +349 -0
- agent_kernel/simulator.py +449 -0
- agent_kernel/teacher.py +85 -0
- agent_kernel/triage.py +152 -0
- agent_os/__init__.py +409 -0
- agent_os/_adversarial_impl.py +200 -0
- agent_os/_circuit_breaker_impl.py +232 -0
- agent_os/_mcp_metrics.py +193 -0
- agent_os/adversarial.py +20 -0
- agent_os/agents_compat.py +490 -0
- agent_os/audit_logger.py +135 -0
- agent_os/base_agent.py +651 -0
- agent_os/circuit_breaker.py +34 -0
- agent_os/cli/__init__.py +659 -0
- agent_os/cli/cmd_audit.py +128 -0
- agent_os/cli/cmd_init.py +152 -0
- agent_os/cli/cmd_policy.py +41 -0
- agent_os/cli/cmd_policy_gen.py +180 -0
- agent_os/cli/cmd_validate.py +258 -0
- agent_os/cli/mcp_scan.py +265 -0
- agent_os/cli/output.py +192 -0
- agent_os/cli/policy_checker.py +330 -0
- agent_os/compat.py +74 -0
- agent_os/constraint_graph.py +234 -0
- agent_os/content_governance.py +140 -0
- agent_os/context_budget.py +305 -0
- agent_os/credential_redactor.py +224 -0
- agent_os/diff_policy.py +89 -0
- agent_os/egress_policy.py +159 -0
- agent_os/escalation.py +276 -0
- agent_os/event_bus.py +124 -0
- agent_os/exceptions.py +180 -0
- agent_os/execution_context_policy.py +141 -0
- agent_os/github_enterprise.py +96 -0
- agent_os/health.py +20 -0
- agent_os/integrations/__init__.py +279 -0
- agent_os/integrations/a2a_adapter.py +279 -0
- agent_os/integrations/agent_lightning/__init__.py +30 -0
- agent_os/integrations/anthropic_adapter.py +420 -0
- agent_os/integrations/autogen_adapter.py +620 -0
- agent_os/integrations/base.py +1137 -0
- agent_os/integrations/compat.py +229 -0
- agent_os/integrations/config.py +98 -0
- agent_os/integrations/conversation_guardian.py +957 -0
- agent_os/integrations/crewai_adapter.py +467 -0
- agent_os/integrations/drift_detector.py +425 -0
- agent_os/integrations/dry_run.py +124 -0
- agent_os/integrations/escalation.py +582 -0
- agent_os/integrations/gemini_adapter.py +364 -0
- agent_os/integrations/google_adk_adapter.py +633 -0
- agent_os/integrations/guardrails_adapter.py +394 -0
- agent_os/integrations/health.py +197 -0
- agent_os/integrations/langchain_adapter.py +654 -0
- agent_os/integrations/llamafirewall.py +343 -0
- agent_os/integrations/llamaindex_adapter.py +188 -0
- agent_os/integrations/logging.py +191 -0
- agent_os/integrations/maf_adapter.py +631 -0
- agent_os/integrations/mistral_adapter.py +365 -0
- agent_os/integrations/openai_adapter.py +816 -0
- agent_os/integrations/openai_agents_sdk.py +406 -0
- agent_os/integrations/policy_compose.py +171 -0
- agent_os/integrations/profiling.py +144 -0
- agent_os/integrations/pydantic_ai_adapter.py +420 -0
- agent_os/integrations/rate_limiter.py +130 -0
- agent_os/integrations/rbac.py +143 -0
- agent_os/integrations/registry.py +113 -0
- agent_os/integrations/scope_guard.py +303 -0
- agent_os/integrations/semantic_kernel_adapter.py +769 -0
- agent_os/integrations/smolagents_adapter.py +629 -0
- agent_os/integrations/templates.py +178 -0
- agent_os/integrations/token_budget.py +134 -0
- agent_os/integrations/tool_aliases.py +190 -0
- agent_os/integrations/webhooks.py +177 -0
- agent_os/lite.py +208 -0
- agent_os/mcp_gateway.py +385 -0
- agent_os/mcp_message_signer.py +273 -0
- agent_os/mcp_protocols.py +161 -0
- agent_os/mcp_response_scanner.py +232 -0
- agent_os/mcp_security.py +924 -0
- agent_os/mcp_session_auth.py +231 -0
- agent_os/mcp_sliding_rate_limiter.py +184 -0
- agent_os/memory_guard.py +409 -0
- agent_os/metrics.py +134 -0
- agent_os/mute.py +428 -0
- agent_os/mute_agent.py +209 -0
- agent_os/policies/__init__.py +77 -0
- agent_os/policies/async_evaluator.py +275 -0
- agent_os/policies/backends.py +670 -0
- agent_os/policies/bridge.py +169 -0
- agent_os/policies/budget.py +85 -0
- agent_os/policies/cli.py +294 -0
- agent_os/policies/conflict_resolution.py +270 -0
- agent_os/policies/data_classification.py +252 -0
- agent_os/policies/evaluator.py +239 -0
- agent_os/policies/policy_schema.json +228 -0
- agent_os/policies/rate_limiting.py +145 -0
- agent_os/policies/schema.py +115 -0
- agent_os/policies/shared.py +331 -0
- agent_os/prompt_injection.py +694 -0
- agent_os/providers.py +182 -0
- agent_os/py.typed +0 -0
- agent_os/retry.py +81 -0
- agent_os/reversibility.py +251 -0
- agent_os/sandbox.py +432 -0
- agent_os/sandbox_provider.py +140 -0
- agent_os/secure_codegen.py +525 -0
- agent_os/security_skills.py +538 -0
- agent_os/semantic_policy.py +422 -0
- agent_os/server/__init__.py +15 -0
- agent_os/server/__main__.py +25 -0
- agent_os/server/app.py +277 -0
- agent_os/server/models.py +104 -0
- agent_os/shift_left_metrics.py +130 -0
- agent_os/stateless.py +742 -0
- agent_os/supervisor.py +148 -0
- agent_os/task_outcome.py +148 -0
- agent_os/transparency.py +181 -0
- agent_os/trust_root.py +128 -0
- agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
- agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
- agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
- agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
- agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
- agent_os_observability/__init__.py +27 -0
- agent_os_observability/dashboards.py +898 -0
- agent_os_observability/metrics.py +398 -0
- agent_os_observability/server.py +223 -0
- agent_os_observability/tracer.py +232 -0
- agent_primitives/__init__.py +24 -0
- agent_primitives/failures.py +84 -0
- agent_primitives/py.typed +0 -0
- amb_core/__init__.py +177 -0
- amb_core/adapters/__init__.py +57 -0
- amb_core/adapters/aws_sqs_broker.py +376 -0
- amb_core/adapters/azure_servicebus_broker.py +340 -0
- amb_core/adapters/kafka_broker.py +260 -0
- amb_core/adapters/nats_broker.py +285 -0
- amb_core/adapters/rabbitmq_broker.py +235 -0
- amb_core/adapters/redis_broker.py +262 -0
- amb_core/broker.py +145 -0
- amb_core/bus.py +481 -0
- amb_core/cloudevents.py +509 -0
- amb_core/dlq.py +345 -0
- amb_core/hf_utils.py +536 -0
- amb_core/memory_broker.py +410 -0
- amb_core/models.py +141 -0
- amb_core/persistence.py +529 -0
- amb_core/schema.py +294 -0
- amb_core/tracing.py +358 -0
- atr/__init__.py +640 -0
- atr/access.py +348 -0
- atr/composition.py +645 -0
- atr/decorator.py +357 -0
- atr/executor.py +384 -0
- atr/health.py +557 -0
- atr/hf_utils.py +449 -0
- atr/injection.py +422 -0
- atr/metrics.py +440 -0
- atr/policies.py +403 -0
- atr/py.typed +2 -0
- atr/registry.py +452 -0
- atr/schema.py +480 -0
- atr/tools/safe/__init__.py +75 -0
- atr/tools/safe/calculator.py +467 -0
- atr/tools/safe/datetime_tool.py +443 -0
- atr/tools/safe/file_reader.py +402 -0
- atr/tools/safe/http_client.py +316 -0
- atr/tools/safe/json_parser.py +374 -0
- atr/tools/safe/text_tool.py +537 -0
- atr/tools/safe/toolkit.py +175 -0
- caas/__init__.py +162 -0
- caas/api/__init__.py +7 -0
- caas/api/server.py +1328 -0
- caas/caching.py +834 -0
- caas/cli.py +210 -0
- caas/conversation.py +223 -0
- caas/decay.py +72 -0
- caas/detection/__init__.py +9 -0
- caas/detection/detector.py +238 -0
- caas/enrichment.py +130 -0
- caas/gateway/__init__.py +27 -0
- caas/gateway/trust_gateway.py +474 -0
- caas/hf_utils.py +479 -0
- caas/ingestion/__init__.py +23 -0
- caas/ingestion/processors.py +253 -0
- caas/ingestion/structure_parser.py +188 -0
- caas/models.py +356 -0
- caas/pragmatic_truth.py +444 -0
- caas/routing/__init__.py +10 -0
- caas/routing/heuristic_router.py +58 -0
- caas/storage/__init__.py +9 -0
- caas/storage/store.py +389 -0
- caas/triad.py +213 -0
- caas/tuning/__init__.py +9 -0
- caas/tuning/tuner.py +329 -0
- caas/vfs/__init__.py +14 -0
- caas/vfs/filesystem.py +452 -0
- cmvk/__init__.py +218 -0
- cmvk/audit.py +402 -0
- cmvk/benchmarks.py +478 -0
- cmvk/constitutional.py +904 -0
- cmvk/hf_utils.py +301 -0
- cmvk/metrics.py +473 -0
- cmvk/profiles.py +300 -0
- cmvk/py.typed +0 -0
- cmvk/types.py +12 -0
- cmvk/verification.py +956 -0
- emk/__init__.py +89 -0
- emk/causal.py +352 -0
- emk/hf_utils.py +421 -0
- emk/indexer.py +83 -0
- emk/py.typed +0 -0
- emk/schema.py +204 -0
- emk/sleep_cycle.py +347 -0
- emk/store.py +281 -0
- iatp/__init__.py +166 -0
- iatp/attestation.py +461 -0
- iatp/cli.py +317 -0
- iatp/hf_utils.py +472 -0
- iatp/ipc_pipes.py +580 -0
- iatp/main.py +412 -0
- iatp/models/__init__.py +447 -0
- iatp/policy_engine.py +337 -0
- iatp/py.typed +2 -0
- iatp/recovery.py +321 -0
- iatp/security/__init__.py +270 -0
- iatp/sidecar/__init__.py +519 -0
- iatp/telemetry/__init__.py +164 -0
- iatp/tests/__init__.py +1 -0
- iatp/tests/test_attestation.py +370 -0
- iatp/tests/test_cli.py +131 -0
- iatp/tests/test_ed25519_attestation.py +211 -0
- iatp/tests/test_models.py +130 -0
- iatp/tests/test_policy_engine.py +347 -0
- iatp/tests/test_recovery.py +281 -0
- iatp/tests/test_security.py +222 -0
- iatp/tests/test_sidecar.py +167 -0
- iatp/tests/test_telemetry.py +175 -0
- mcp_kernel_server/__init__.py +28 -0
- mcp_kernel_server/cli.py +274 -0
- mcp_kernel_server/resources.py +217 -0
- mcp_kernel_server/server.py +564 -0
- mcp_kernel_server/tools.py +1174 -0
- mute_agent/__init__.py +68 -0
- mute_agent/core/__init__.py +1 -0
- mute_agent/core/execution_agent.py +166 -0
- mute_agent/core/handshake_protocol.py +201 -0
- mute_agent/core/reasoning_agent.py +238 -0
- mute_agent/knowledge_graph/__init__.py +1 -0
- mute_agent/knowledge_graph/graph_elements.py +65 -0
- mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
- mute_agent/knowledge_graph/subgraph.py +224 -0
- mute_agent/listener/__init__.py +43 -0
- mute_agent/listener/adapters/__init__.py +31 -0
- mute_agent/listener/adapters/base_adapter.py +189 -0
- mute_agent/listener/adapters/caas_adapter.py +344 -0
- mute_agent/listener/adapters/control_plane_adapter.py +436 -0
- mute_agent/listener/adapters/iatp_adapter.py +332 -0
- mute_agent/listener/adapters/scak_adapter.py +251 -0
- mute_agent/listener/listener.py +610 -0
- mute_agent/listener/state_observer.py +436 -0
- mute_agent/listener/threshold_config.py +313 -0
- mute_agent/super_system/__init__.py +1 -0
- mute_agent/super_system/router.py +204 -0
- mute_agent/visualization/__init__.py +10 -0
- mute_agent/visualization/graph_debugger.py +502 -0
- nexus/README.md +60 -0
- nexus/__init__.py +51 -0
- nexus/arbiter.py +359 -0
- nexus/client.py +466 -0
- nexus/dmz.py +444 -0
- nexus/escrow.py +430 -0
- nexus/exceptions.py +286 -0
- nexus/pyproject.toml +36 -0
- nexus/registry.py +393 -0
- nexus/reputation.py +425 -0
- nexus/schemas/__init__.py +51 -0
- nexus/schemas/compliance.py +276 -0
- nexus/schemas/escrow.py +251 -0
- nexus/schemas/manifest.py +225 -0
- nexus/schemas/receipt.py +208 -0
- nexus/tests/__init__.py +0 -0
- nexus/tests/conftest.py +146 -0
- nexus/tests/test_arbiter.py +192 -0
- nexus/tests/test_dmz.py +194 -0
- nexus/tests/test_escrow.py +276 -0
- nexus/tests/test_exceptions.py +225 -0
- nexus/tests/test_registry.py +232 -0
- nexus/tests/test_reputation.py +328 -0
- nexus/tests/test_schemas.py +295 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Agent Virtual File System (VFS) - POSIX-style memory abstraction for agents.
|
|
5
|
+
|
|
6
|
+
This module provides a virtual file system interface for agent memory,
|
|
7
|
+
inspired by POSIX VFS but designed for AI agent state management.
|
|
8
|
+
|
|
9
|
+
Instead of high-level "Semantic Memory" or "Vector Store" abstractions,
|
|
10
|
+
this provides a standard POSIX-like interface that can mount ANY backend.
|
|
11
|
+
|
|
12
|
+
Mount Points:
|
|
13
|
+
/mem/working - Working memory (current context, scratchpad)
|
|
14
|
+
/mem/episodic - Episodic memory (past interactions, experiences)
|
|
15
|
+
/mem/semantic - Semantic memory (facts, knowledge)
|
|
16
|
+
/mem/procedural - Procedural memory (learned skills, patterns)
|
|
17
|
+
/state - Agent state (checkpoints, snapshots)
|
|
18
|
+
/tools - Tool interfaces (mounted dynamically)
|
|
19
|
+
/policy - Policy files (read-only from user-space)
|
|
20
|
+
|
|
21
|
+
Design Philosophy:
|
|
22
|
+
- Everything is a file (UNIX philosophy)
|
|
23
|
+
- Backends are drivers (Pinecone, Weaviate, Redis = mount points)
|
|
24
|
+
- Kernel controls mount permissions
|
|
25
|
+
- User-space agents see unified interface
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from abc import ABC, abstractmethod
|
|
29
|
+
from dataclasses import dataclass, field
|
|
30
|
+
from datetime import datetime, timezone
|
|
31
|
+
from enum import IntFlag, auto
|
|
32
|
+
from pathlib import PurePosixPath
|
|
33
|
+
from typing import (
|
|
34
|
+
Any, Dict, List, Optional, Union, Iterator, BinaryIO, TextIO, Callable
|
|
35
|
+
)
|
|
36
|
+
import io
|
|
37
|
+
import json
|
|
38
|
+
import logging
|
|
39
|
+
import hashlib
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class FileMode(IntFlag):
|
|
45
|
+
"""File permission modes (POSIX-style)."""
|
|
46
|
+
NONE = 0
|
|
47
|
+
READ = auto() # r
|
|
48
|
+
WRITE = auto() # w
|
|
49
|
+
EXECUTE = auto() # x (for tools/procedures)
|
|
50
|
+
APPEND = auto() # a
|
|
51
|
+
|
|
52
|
+
# Common combinations
|
|
53
|
+
RO = READ
|
|
54
|
+
RW = READ | WRITE
|
|
55
|
+
RWX = READ | WRITE | EXECUTE
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class FileType(IntFlag):
|
|
59
|
+
"""File types in the VFS."""
|
|
60
|
+
REGULAR = auto() # Regular data file
|
|
61
|
+
DIRECTORY = auto() # Directory
|
|
62
|
+
SYMLINK = auto() # Symbolic link
|
|
63
|
+
DEVICE = auto() # Device file (backend connection)
|
|
64
|
+
SOCKET = auto() # IPC socket
|
|
65
|
+
FIFO = auto() # Named pipe
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class INode:
|
|
70
|
+
"""
|
|
71
|
+
Index node - metadata for a VFS entry.
|
|
72
|
+
|
|
73
|
+
Inspired by UNIX inodes but adapted for agent memory.
|
|
74
|
+
"""
|
|
75
|
+
path: str
|
|
76
|
+
file_type: FileType
|
|
77
|
+
mode: FileMode
|
|
78
|
+
size: int = 0
|
|
79
|
+
created: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
80
|
+
modified: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
81
|
+
accessed: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
82
|
+
owner: str = "agent"
|
|
83
|
+
group: str = "agents"
|
|
84
|
+
|
|
85
|
+
# Extended attributes (agent-specific)
|
|
86
|
+
embedding_dim: Optional[int] = None # For vector entries
|
|
87
|
+
content_hash: Optional[str] = None
|
|
88
|
+
ttl_seconds: Optional[int] = None # Time-to-live
|
|
89
|
+
tags: List[str] = field(default_factory=list)
|
|
90
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
91
|
+
|
|
92
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
93
|
+
return {
|
|
94
|
+
"path": self.path,
|
|
95
|
+
"type": self.file_type.name,
|
|
96
|
+
"mode": self.mode.value,
|
|
97
|
+
"size": self.size,
|
|
98
|
+
"created": self.created.isoformat(),
|
|
99
|
+
"modified": self.modified.isoformat(),
|
|
100
|
+
"owner": self.owner,
|
|
101
|
+
"metadata": self.metadata,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class FileDescriptor:
|
|
107
|
+
"""Open file descriptor."""
|
|
108
|
+
fd: int
|
|
109
|
+
path: str
|
|
110
|
+
mode: FileMode
|
|
111
|
+
position: int = 0
|
|
112
|
+
inode: Optional[INode] = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class VFSBackend(ABC):
|
|
116
|
+
"""
|
|
117
|
+
Abstract backend driver for VFS mount points.
|
|
118
|
+
|
|
119
|
+
Implement this to add support for different storage backends:
|
|
120
|
+
- MemoryBackend: In-memory storage (default)
|
|
121
|
+
- RedisBackend: Redis-based persistent storage
|
|
122
|
+
- VectorBackend: Vector database (Pinecone, Weaviate, etc.)
|
|
123
|
+
- SQLBackend: SQL database storage
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
@abstractmethod
|
|
127
|
+
def read(self, path: str) -> bytes:
|
|
128
|
+
"""Read file contents."""
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
@abstractmethod
|
|
132
|
+
def write(self, path: str, data: bytes, mode: FileMode = FileMode.WRITE) -> int:
|
|
133
|
+
"""Write data to file. Returns bytes written."""
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
@abstractmethod
|
|
137
|
+
def delete(self, path: str) -> bool:
|
|
138
|
+
"""Delete a file."""
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
@abstractmethod
|
|
142
|
+
def exists(self, path: str) -> bool:
|
|
143
|
+
"""Check if path exists."""
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def list_dir(self, path: str) -> List[str]:
|
|
148
|
+
"""List directory contents."""
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
@abstractmethod
|
|
152
|
+
def stat(self, path: str) -> Optional[INode]:
|
|
153
|
+
"""Get file metadata."""
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def mkdir(self, path: str) -> bool:
|
|
158
|
+
"""Create directory."""
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class MemoryBackend(VFSBackend):
|
|
163
|
+
"""
|
|
164
|
+
In-memory VFS backend.
|
|
165
|
+
|
|
166
|
+
Simple implementation for working memory and testing.
|
|
167
|
+
Data is lost on agent restart (ephemeral by design).
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
def __init__(self):
|
|
171
|
+
self._files: Dict[str, bytes] = {}
|
|
172
|
+
self._inodes: Dict[str, INode] = {}
|
|
173
|
+
self._dirs: set = {"/"}
|
|
174
|
+
|
|
175
|
+
def read(self, path: str) -> bytes:
|
|
176
|
+
if path not in self._files:
|
|
177
|
+
raise FileNotFoundError(f"No such file: {path}")
|
|
178
|
+
|
|
179
|
+
# Update access time
|
|
180
|
+
if path in self._inodes:
|
|
181
|
+
self._inodes[path].accessed = datetime.now(timezone.utc)
|
|
182
|
+
|
|
183
|
+
return self._files[path]
|
|
184
|
+
|
|
185
|
+
def write(self, path: str, data: bytes, mode: FileMode = FileMode.WRITE) -> int:
|
|
186
|
+
# Ensure parent directory exists - auto-create if needed
|
|
187
|
+
parent = str(PurePosixPath(path).parent)
|
|
188
|
+
if parent not in self._dirs and parent != path:
|
|
189
|
+
# Auto-create parent directories (like mkdir -p)
|
|
190
|
+
self._mkdir_p(parent)
|
|
191
|
+
|
|
192
|
+
if mode & FileMode.APPEND and path in self._files:
|
|
193
|
+
self._files[path] += data
|
|
194
|
+
else:
|
|
195
|
+
self._files[path] = data
|
|
196
|
+
|
|
197
|
+
# Update or create inode
|
|
198
|
+
now = datetime.now(timezone.utc)
|
|
199
|
+
if path in self._inodes:
|
|
200
|
+
self._inodes[path].modified = now
|
|
201
|
+
self._inodes[path].size = len(self._files[path])
|
|
202
|
+
self._inodes[path].content_hash = hashlib.sha256(self._files[path]).hexdigest()[:16]
|
|
203
|
+
else:
|
|
204
|
+
self._inodes[path] = INode(
|
|
205
|
+
path=path,
|
|
206
|
+
file_type=FileType.REGULAR,
|
|
207
|
+
mode=FileMode.RW,
|
|
208
|
+
size=len(data),
|
|
209
|
+
content_hash=hashlib.sha256(data).hexdigest()[:16],
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
return len(data)
|
|
213
|
+
|
|
214
|
+
def delete(self, path: str) -> bool:
|
|
215
|
+
if path in self._files:
|
|
216
|
+
del self._files[path]
|
|
217
|
+
if path in self._inodes:
|
|
218
|
+
del self._inodes[path]
|
|
219
|
+
return True
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
def exists(self, path: str) -> bool:
|
|
223
|
+
return path in self._files or path in self._dirs
|
|
224
|
+
|
|
225
|
+
def list_dir(self, path: str) -> List[str]:
|
|
226
|
+
if path not in self._dirs:
|
|
227
|
+
raise NotADirectoryError(f"Not a directory: {path}")
|
|
228
|
+
|
|
229
|
+
# Find all entries under this directory
|
|
230
|
+
prefix = path.rstrip("/") + "/"
|
|
231
|
+
entries = set()
|
|
232
|
+
|
|
233
|
+
for p in list(self._files.keys()) + list(self._dirs):
|
|
234
|
+
if p.startswith(prefix):
|
|
235
|
+
# Get the immediate child
|
|
236
|
+
remainder = p[len(prefix):]
|
|
237
|
+
if remainder:
|
|
238
|
+
child = remainder.split("/")[0]
|
|
239
|
+
entries.add(child)
|
|
240
|
+
|
|
241
|
+
return sorted(entries)
|
|
242
|
+
|
|
243
|
+
def stat(self, path: str) -> Optional[INode]:
|
|
244
|
+
if path in self._inodes:
|
|
245
|
+
return self._inodes[path]
|
|
246
|
+
if path in self._dirs:
|
|
247
|
+
return INode(
|
|
248
|
+
path=path,
|
|
249
|
+
file_type=FileType.DIRECTORY,
|
|
250
|
+
mode=FileMode.RWX,
|
|
251
|
+
)
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
def mkdir(self, path: str) -> bool:
|
|
255
|
+
if path in self._dirs:
|
|
256
|
+
return False
|
|
257
|
+
self._dirs.add(path)
|
|
258
|
+
return True
|
|
259
|
+
|
|
260
|
+
def _mkdir_p(self, path: str) -> None:
|
|
261
|
+
"""Create directory and all parent directories (like mkdir -p)."""
|
|
262
|
+
parts = path.strip("/").split("/")
|
|
263
|
+
current = ""
|
|
264
|
+
for part in parts:
|
|
265
|
+
current = current + "/" + part
|
|
266
|
+
if current not in self._dirs:
|
|
267
|
+
self._dirs.add(current)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass
|
|
271
|
+
class MountPoint:
|
|
272
|
+
"""A mounted filesystem."""
|
|
273
|
+
path: str
|
|
274
|
+
backend: VFSBackend
|
|
275
|
+
mode: FileMode = FileMode.RW
|
|
276
|
+
read_only: bool = False
|
|
277
|
+
description: str = ""
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class AgentVFS:
|
|
281
|
+
"""
|
|
282
|
+
Agent Virtual File System.
|
|
283
|
+
|
|
284
|
+
Provides a unified POSIX-like interface for agent memory,
|
|
285
|
+
with support for multiple backends mounted at different paths.
|
|
286
|
+
|
|
287
|
+
Example:
|
|
288
|
+
vfs = AgentVFS(agent_id="agent-001")
|
|
289
|
+
|
|
290
|
+
# Mount backends
|
|
291
|
+
vfs.mount("/mem/working", MemoryBackend())
|
|
292
|
+
vfs.mount("/mem/episodic", RedisBackend(host="localhost"))
|
|
293
|
+
vfs.mount("/mem/semantic", VectorBackend(client=pinecone_client))
|
|
294
|
+
|
|
295
|
+
# Use like a filesystem
|
|
296
|
+
vfs.write("/mem/working/scratchpad.txt", b"Current task: ...")
|
|
297
|
+
vfs.write("/mem/episodic/2024-01-26/interaction-001.json", data)
|
|
298
|
+
|
|
299
|
+
# Read back
|
|
300
|
+
data = vfs.read("/mem/working/scratchpad.txt")
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
# Standard mount points for agents
|
|
304
|
+
STANDARD_MOUNTS = {
|
|
305
|
+
"/mem/working": "Working memory (ephemeral context)",
|
|
306
|
+
"/mem/episodic": "Episodic memory (experiences)",
|
|
307
|
+
"/mem/semantic": "Semantic memory (facts)",
|
|
308
|
+
"/mem/procedural": "Procedural memory (skills)",
|
|
309
|
+
"/state": "Agent state (checkpoints)",
|
|
310
|
+
"/tools": "Tool interfaces",
|
|
311
|
+
"/policy": "Policy files (read-only)",
|
|
312
|
+
"/ipc": "Inter-process communication",
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
def __init__(self, agent_id: str):
|
|
316
|
+
self.agent_id = agent_id
|
|
317
|
+
self._mounts: Dict[str, MountPoint] = {}
|
|
318
|
+
self._fd_counter = 0
|
|
319
|
+
self._open_files: Dict[int, FileDescriptor] = {}
|
|
320
|
+
|
|
321
|
+
# Create standard mount points with default memory backend
|
|
322
|
+
self._init_standard_mounts()
|
|
323
|
+
|
|
324
|
+
def _init_standard_mounts(self) -> None:
|
|
325
|
+
"""Initialize standard mount points with memory backend."""
|
|
326
|
+
default_backend = MemoryBackend()
|
|
327
|
+
|
|
328
|
+
for path, description in self.STANDARD_MOUNTS.items():
|
|
329
|
+
read_only = path == "/policy" # Policy is read-only from user-space
|
|
330
|
+
self._mounts[path] = MountPoint(
|
|
331
|
+
path=path,
|
|
332
|
+
backend=default_backend,
|
|
333
|
+
mode=FileMode.RO if read_only else FileMode.RW,
|
|
334
|
+
read_only=read_only,
|
|
335
|
+
description=description,
|
|
336
|
+
)
|
|
337
|
+
# Create the directory
|
|
338
|
+
default_backend.mkdir(path)
|
|
339
|
+
|
|
340
|
+
def mount(
|
|
341
|
+
self,
|
|
342
|
+
path: str,
|
|
343
|
+
backend: VFSBackend,
|
|
344
|
+
mode: FileMode = FileMode.RW,
|
|
345
|
+
read_only: bool = False,
|
|
346
|
+
) -> None:
|
|
347
|
+
"""
|
|
348
|
+
Mount a backend at the specified path.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
path: Mount point (e.g., "/mem/semantic")
|
|
352
|
+
backend: VFS backend implementation
|
|
353
|
+
mode: Access mode
|
|
354
|
+
read_only: If True, writes are rejected
|
|
355
|
+
"""
|
|
356
|
+
logger.info(f"[VFS] Mounting {backend.__class__.__name__} at {path}")
|
|
357
|
+
|
|
358
|
+
self._mounts[path] = MountPoint(
|
|
359
|
+
path=path,
|
|
360
|
+
backend=backend,
|
|
361
|
+
mode=mode,
|
|
362
|
+
read_only=read_only,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Ensure mount point directory exists
|
|
366
|
+
backend.mkdir(path)
|
|
367
|
+
|
|
368
|
+
def unmount(self, path: str) -> bool:
|
|
369
|
+
"""Unmount a filesystem."""
|
|
370
|
+
if path in self._mounts:
|
|
371
|
+
logger.info(f"[VFS] Unmounting {path}")
|
|
372
|
+
del self._mounts[path]
|
|
373
|
+
return True
|
|
374
|
+
return False
|
|
375
|
+
|
|
376
|
+
def _resolve_mount(self, path: str) -> tuple[MountPoint, str]:
|
|
377
|
+
"""
|
|
378
|
+
Resolve a path to its mount point and relative path.
|
|
379
|
+
|
|
380
|
+
Returns (mount_point, relative_path)
|
|
381
|
+
"""
|
|
382
|
+
# Find the longest matching mount point
|
|
383
|
+
best_match = None
|
|
384
|
+
best_len = 0
|
|
385
|
+
|
|
386
|
+
for mount_path in self._mounts:
|
|
387
|
+
if path.startswith(mount_path) and len(mount_path) > best_len:
|
|
388
|
+
best_match = mount_path
|
|
389
|
+
best_len = len(mount_path)
|
|
390
|
+
|
|
391
|
+
if not best_match:
|
|
392
|
+
raise FileNotFoundError(f"No mount point for path: {path}")
|
|
393
|
+
|
|
394
|
+
mount = self._mounts[best_match]
|
|
395
|
+
return mount, path
|
|
396
|
+
|
|
397
|
+
# ========== File Operations ==========
|
|
398
|
+
|
|
399
|
+
def read(self, path: str) -> bytes:
|
|
400
|
+
"""Read file contents."""
|
|
401
|
+
mount, full_path = self._resolve_mount(path)
|
|
402
|
+
return mount.backend.read(full_path)
|
|
403
|
+
|
|
404
|
+
def read_text(self, path: str, encoding: str = "utf-8") -> str:
|
|
405
|
+
"""Read file as text."""
|
|
406
|
+
return self.read(path).decode(encoding)
|
|
407
|
+
|
|
408
|
+
def read_json(self, path: str) -> Any:
|
|
409
|
+
"""Read and parse JSON file."""
|
|
410
|
+
return json.loads(self.read_text(path))
|
|
411
|
+
|
|
412
|
+
def write(self, path: str, data: Union[bytes, str], mode: FileMode = FileMode.WRITE) -> int:
|
|
413
|
+
"""Write data to file."""
|
|
414
|
+
mount, full_path = self._resolve_mount(path)
|
|
415
|
+
|
|
416
|
+
if mount.read_only:
|
|
417
|
+
raise PermissionError(f"Mount point is read-only: {mount.path}")
|
|
418
|
+
|
|
419
|
+
if isinstance(data, str):
|
|
420
|
+
data = data.encode("utf-8")
|
|
421
|
+
|
|
422
|
+
return mount.backend.write(full_path, data, mode)
|
|
423
|
+
|
|
424
|
+
def write_json(self, path: str, data: Any, indent: int = 2) -> int:
|
|
425
|
+
"""Write data as JSON."""
|
|
426
|
+
return self.write(path, json.dumps(data, indent=indent, default=str))
|
|
427
|
+
|
|
428
|
+
def append(self, path: str, data: Union[bytes, str]) -> int:
|
|
429
|
+
"""Append data to file."""
|
|
430
|
+
return self.write(path, data, FileMode.APPEND)
|
|
431
|
+
|
|
432
|
+
def delete(self, path: str) -> bool:
|
|
433
|
+
"""Delete a file."""
|
|
434
|
+
mount, full_path = self._resolve_mount(path)
|
|
435
|
+
|
|
436
|
+
if mount.read_only:
|
|
437
|
+
raise PermissionError(f"Mount point is read-only: {mount.path}")
|
|
438
|
+
|
|
439
|
+
return mount.backend.delete(full_path)
|
|
440
|
+
|
|
441
|
+
def exists(self, path: str) -> bool:
|
|
442
|
+
"""Check if path exists."""
|
|
443
|
+
try:
|
|
444
|
+
mount, full_path = self._resolve_mount(path)
|
|
445
|
+
return mount.backend.exists(full_path)
|
|
446
|
+
except FileNotFoundError:
|
|
447
|
+
return False
|
|
448
|
+
|
|
449
|
+
def stat(self, path: str) -> Optional[INode]:
|
|
450
|
+
"""Get file metadata."""
|
|
451
|
+
mount, full_path = self._resolve_mount(path)
|
|
452
|
+
return mount.backend.stat(full_path)
|
|
453
|
+
|
|
454
|
+
def ls(self, path: str = "/") -> List[str]:
|
|
455
|
+
"""List directory contents."""
|
|
456
|
+
mount, full_path = self._resolve_mount(path)
|
|
457
|
+
return mount.backend.list_dir(full_path)
|
|
458
|
+
|
|
459
|
+
def mkdir(self, path: str) -> bool:
|
|
460
|
+
"""Create directory."""
|
|
461
|
+
mount, full_path = self._resolve_mount(path)
|
|
462
|
+
|
|
463
|
+
if mount.read_only:
|
|
464
|
+
raise PermissionError(f"Mount point is read-only: {mount.path}")
|
|
465
|
+
|
|
466
|
+
return mount.backend.mkdir(full_path)
|
|
467
|
+
|
|
468
|
+
# ========== File Descriptor Operations (POSIX-style) ==========
|
|
469
|
+
|
|
470
|
+
def open(self, path: str, mode: FileMode = FileMode.READ) -> int:
|
|
471
|
+
"""
|
|
472
|
+
Open a file and return a file descriptor.
|
|
473
|
+
|
|
474
|
+
This provides a more traditional POSIX-style interface.
|
|
475
|
+
"""
|
|
476
|
+
mount, full_path = self._resolve_mount(path)
|
|
477
|
+
|
|
478
|
+
if mode & FileMode.WRITE and mount.read_only:
|
|
479
|
+
raise PermissionError(f"Cannot open for writing: {path}")
|
|
480
|
+
|
|
481
|
+
inode = mount.backend.stat(full_path)
|
|
482
|
+
|
|
483
|
+
self._fd_counter += 1
|
|
484
|
+
fd = self._fd_counter
|
|
485
|
+
|
|
486
|
+
self._open_files[fd] = FileDescriptor(
|
|
487
|
+
fd=fd,
|
|
488
|
+
path=full_path,
|
|
489
|
+
mode=mode,
|
|
490
|
+
inode=inode,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
return fd
|
|
494
|
+
|
|
495
|
+
def close(self, fd: int) -> None:
|
|
496
|
+
"""Close a file descriptor."""
|
|
497
|
+
if fd in self._open_files:
|
|
498
|
+
del self._open_files[fd]
|
|
499
|
+
|
|
500
|
+
def fd_read(self, fd: int, size: int = -1) -> bytes:
|
|
501
|
+
"""Read from file descriptor."""
|
|
502
|
+
if fd not in self._open_files:
|
|
503
|
+
raise ValueError(f"Invalid file descriptor: {fd}")
|
|
504
|
+
|
|
505
|
+
desc = self._open_files[fd]
|
|
506
|
+
data = self.read(desc.path)
|
|
507
|
+
|
|
508
|
+
if size < 0:
|
|
509
|
+
return data[desc.position:]
|
|
510
|
+
|
|
511
|
+
result = data[desc.position:desc.position + size]
|
|
512
|
+
desc.position += len(result)
|
|
513
|
+
return result
|
|
514
|
+
|
|
515
|
+
def fd_write(self, fd: int, data: bytes) -> int:
|
|
516
|
+
"""Write to file descriptor."""
|
|
517
|
+
if fd not in self._open_files:
|
|
518
|
+
raise ValueError(f"Invalid file descriptor: {fd}")
|
|
519
|
+
|
|
520
|
+
desc = self._open_files[fd]
|
|
521
|
+
if not (desc.mode & FileMode.WRITE):
|
|
522
|
+
raise PermissionError("File not opened for writing")
|
|
523
|
+
|
|
524
|
+
return self.write(desc.path, data)
|
|
525
|
+
|
|
526
|
+
# ========== Memory-Specific Operations ==========
|
|
527
|
+
|
|
528
|
+
def save_checkpoint(self, checkpoint_id: str, state: Dict[str, Any]) -> str:
|
|
529
|
+
"""Save agent state checkpoint."""
|
|
530
|
+
path = f"/state/checkpoints/{checkpoint_id}.json"
|
|
531
|
+
self.write_json(path, {
|
|
532
|
+
"checkpoint_id": checkpoint_id,
|
|
533
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
534
|
+
"agent_id": self.agent_id,
|
|
535
|
+
"state": state,
|
|
536
|
+
})
|
|
537
|
+
logger.info(f"[VFS] Saved checkpoint: {checkpoint_id}")
|
|
538
|
+
return path
|
|
539
|
+
|
|
540
|
+
def load_checkpoint(self, checkpoint_id: str) -> Dict[str, Any]:
|
|
541
|
+
"""Load agent state from checkpoint."""
|
|
542
|
+
path = f"/state/checkpoints/{checkpoint_id}.json"
|
|
543
|
+
data = self.read_json(path)
|
|
544
|
+
logger.info(f"[VFS] Loaded checkpoint: {checkpoint_id}")
|
|
545
|
+
return data.get("state", {})
|
|
546
|
+
|
|
547
|
+
def log_episodic(self, event: Dict[str, Any], event_id: Optional[str] = None) -> str:
|
|
548
|
+
"""Log an episodic memory event."""
|
|
549
|
+
if not event_id:
|
|
550
|
+
event_id = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S-%f")
|
|
551
|
+
|
|
552
|
+
path = f"/mem/episodic/{event_id}.json"
|
|
553
|
+
self.write_json(path, {
|
|
554
|
+
"event_id": event_id,
|
|
555
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
556
|
+
"event": event,
|
|
557
|
+
})
|
|
558
|
+
return path
|
|
559
|
+
|
|
560
|
+
def get_working_memory(self) -> Dict[str, Any]:
|
|
561
|
+
"""Get all working memory contents."""
|
|
562
|
+
result = {}
|
|
563
|
+
for name in self.ls("/mem/working"):
|
|
564
|
+
path = f"/mem/working/{name}"
|
|
565
|
+
try:
|
|
566
|
+
if name.endswith(".json"):
|
|
567
|
+
result[name] = self.read_json(path)
|
|
568
|
+
else:
|
|
569
|
+
result[name] = self.read_text(path)
|
|
570
|
+
except Exception:
|
|
571
|
+
result[name] = f"<binary: {self.stat(path).size if self.stat(path) else '?'} bytes>"
|
|
572
|
+
return result
|
|
573
|
+
|
|
574
|
+
def clear_working_memory(self) -> int:
|
|
575
|
+
"""Clear working memory. Returns number of files deleted."""
|
|
576
|
+
count = 0
|
|
577
|
+
for name in self.ls("/mem/working"):
|
|
578
|
+
if self.delete(f"/mem/working/{name}"):
|
|
579
|
+
count += 1
|
|
580
|
+
return count
|
|
581
|
+
|
|
582
|
+
def get_mount_info(self) -> List[Dict[str, Any]]:
|
|
583
|
+
"""Get information about all mount points."""
|
|
584
|
+
return [
|
|
585
|
+
{
|
|
586
|
+
"path": mp.path,
|
|
587
|
+
"backend": mp.backend.__class__.__name__,
|
|
588
|
+
"mode": mp.mode.name,
|
|
589
|
+
"read_only": mp.read_only,
|
|
590
|
+
"description": mp.description or self.STANDARD_MOUNTS.get(mp.path, ""),
|
|
591
|
+
}
|
|
592
|
+
for mp in self._mounts.values()
|
|
593
|
+
]
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
# ========== Backend Implementations ==========
|
|
597
|
+
|
|
598
|
+
class VectorBackend(VFSBackend):
|
|
599
|
+
"""
|
|
600
|
+
Vector database backend stub.
|
|
601
|
+
|
|
602
|
+
This is a placeholder for vector store integration.
|
|
603
|
+
Implement with actual Pinecone/Weaviate/Qdrant client.
|
|
604
|
+
"""
|
|
605
|
+
|
|
606
|
+
def __init__(self, client: Any = None, namespace: str = "default"):
|
|
607
|
+
self.client = client
|
|
608
|
+
self.namespace = namespace
|
|
609
|
+
self._fallback = MemoryBackend() # Fallback for non-vector operations
|
|
610
|
+
logger.info(f"[VectorBackend] Initialized with namespace: {namespace}")
|
|
611
|
+
|
|
612
|
+
def read(self, path: str) -> bytes:
|
|
613
|
+
# For vector stores, reading returns the stored document
|
|
614
|
+
return self._fallback.read(path)
|
|
615
|
+
|
|
616
|
+
def write(self, path: str, data: bytes, mode: FileMode = FileMode.WRITE) -> int:
|
|
617
|
+
# For vector stores, this would upsert to the index
|
|
618
|
+
# Actual implementation would embed and store
|
|
619
|
+
return self._fallback.write(path, data, mode)
|
|
620
|
+
|
|
621
|
+
def delete(self, path: str) -> bool:
|
|
622
|
+
return self._fallback.delete(path)
|
|
623
|
+
|
|
624
|
+
def exists(self, path: str) -> bool:
|
|
625
|
+
return self._fallback.exists(path)
|
|
626
|
+
|
|
627
|
+
def list_dir(self, path: str) -> List[str]:
|
|
628
|
+
return self._fallback.list_dir(path)
|
|
629
|
+
|
|
630
|
+
def stat(self, path: str) -> Optional[INode]:
|
|
631
|
+
return self._fallback.stat(path)
|
|
632
|
+
|
|
633
|
+
def mkdir(self, path: str) -> bool:
|
|
634
|
+
return self._fallback.mkdir(path)
|
|
635
|
+
|
|
636
|
+
# Vector-specific methods
|
|
637
|
+
def search(
|
|
638
|
+
self,
|
|
639
|
+
query_vector: List[float],
|
|
640
|
+
top_k: int = 10,
|
|
641
|
+
filter_dict: Optional[Dict[str, Any]] = None,
|
|
642
|
+
) -> List[Dict[str, Any]]:
|
|
643
|
+
"""
|
|
644
|
+
Search for similar vectors.
|
|
645
|
+
|
|
646
|
+
Override with actual vector search implementation.
|
|
647
|
+
"""
|
|
648
|
+
logger.warning("[VectorBackend] search() not implemented - using stub")
|
|
649
|
+
return []
|
|
650
|
+
|
|
651
|
+
def embed_and_store(
|
|
652
|
+
self,
|
|
653
|
+
path: str,
|
|
654
|
+
text: str,
|
|
655
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
656
|
+
) -> str:
|
|
657
|
+
"""
|
|
658
|
+
Embed text and store in vector database.
|
|
659
|
+
|
|
660
|
+
Override with actual embedding implementation.
|
|
661
|
+
"""
|
|
662
|
+
logger.warning("[VectorBackend] embed_and_store() not implemented - using stub")
|
|
663
|
+
return self._fallback.write(path, text.encode("utf-8"))
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
# ========== Convenience Functions ==========
|
|
667
|
+
|
|
668
|
+
def create_agent_vfs(
|
|
669
|
+
agent_id: str,
|
|
670
|
+
working_backend: Optional[VFSBackend] = None,
|
|
671
|
+
episodic_backend: Optional[VFSBackend] = None,
|
|
672
|
+
semantic_backend: Optional[VFSBackend] = None,
|
|
673
|
+
) -> AgentVFS:
|
|
674
|
+
"""
|
|
675
|
+
Create an AgentVFS with optional custom backends.
|
|
676
|
+
|
|
677
|
+
Args:
|
|
678
|
+
agent_id: Unique agent identifier
|
|
679
|
+
working_backend: Backend for /mem/working (default: MemoryBackend)
|
|
680
|
+
episodic_backend: Backend for /mem/episodic (default: MemoryBackend)
|
|
681
|
+
semantic_backend: Backend for /mem/semantic (default: MemoryBackend)
|
|
682
|
+
|
|
683
|
+
Returns:
|
|
684
|
+
Configured AgentVFS instance
|
|
685
|
+
"""
|
|
686
|
+
vfs = AgentVFS(agent_id)
|
|
687
|
+
|
|
688
|
+
if working_backend:
|
|
689
|
+
vfs.mount("/mem/working", working_backend)
|
|
690
|
+
|
|
691
|
+
if episodic_backend:
|
|
692
|
+
vfs.mount("/mem/episodic", episodic_backend)
|
|
693
|
+
|
|
694
|
+
if semantic_backend:
|
|
695
|
+
vfs.mount("/mem/semantic", semantic_backend)
|
|
696
|
+
|
|
697
|
+
return vfs
|