aury-agent 0.0.4__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.
- aury/__init__.py +2 -0
- aury/agents/__init__.py +55 -0
- aury/agents/a2a/__init__.py +168 -0
- aury/agents/backends/__init__.py +196 -0
- aury/agents/backends/artifact/__init__.py +9 -0
- aury/agents/backends/artifact/memory.py +130 -0
- aury/agents/backends/artifact/types.py +133 -0
- aury/agents/backends/code/__init__.py +65 -0
- aury/agents/backends/file/__init__.py +11 -0
- aury/agents/backends/file/local.py +66 -0
- aury/agents/backends/file/types.py +40 -0
- aury/agents/backends/invocation/__init__.py +8 -0
- aury/agents/backends/invocation/memory.py +81 -0
- aury/agents/backends/invocation/types.py +110 -0
- aury/agents/backends/memory/__init__.py +8 -0
- aury/agents/backends/memory/memory.py +179 -0
- aury/agents/backends/memory/types.py +136 -0
- aury/agents/backends/message/__init__.py +9 -0
- aury/agents/backends/message/memory.py +122 -0
- aury/agents/backends/message/types.py +124 -0
- aury/agents/backends/sandbox.py +275 -0
- aury/agents/backends/session/__init__.py +8 -0
- aury/agents/backends/session/memory.py +93 -0
- aury/agents/backends/session/types.py +124 -0
- aury/agents/backends/shell/__init__.py +11 -0
- aury/agents/backends/shell/local.py +110 -0
- aury/agents/backends/shell/types.py +55 -0
- aury/agents/backends/shell.py +209 -0
- aury/agents/backends/snapshot/__init__.py +19 -0
- aury/agents/backends/snapshot/git.py +95 -0
- aury/agents/backends/snapshot/hybrid.py +125 -0
- aury/agents/backends/snapshot/memory.py +86 -0
- aury/agents/backends/snapshot/types.py +59 -0
- aury/agents/backends/state/__init__.py +29 -0
- aury/agents/backends/state/composite.py +49 -0
- aury/agents/backends/state/file.py +57 -0
- aury/agents/backends/state/memory.py +52 -0
- aury/agents/backends/state/sqlite.py +262 -0
- aury/agents/backends/state/types.py +178 -0
- aury/agents/backends/subagent/__init__.py +165 -0
- aury/agents/cli/__init__.py +41 -0
- aury/agents/cli/chat.py +239 -0
- aury/agents/cli/config.py +236 -0
- aury/agents/cli/extensions.py +460 -0
- aury/agents/cli/main.py +189 -0
- aury/agents/cli/session.py +337 -0
- aury/agents/cli/workflow.py +276 -0
- aury/agents/context_providers/__init__.py +66 -0
- aury/agents/context_providers/artifact.py +299 -0
- aury/agents/context_providers/base.py +177 -0
- aury/agents/context_providers/memory.py +70 -0
- aury/agents/context_providers/message.py +130 -0
- aury/agents/context_providers/skill.py +50 -0
- aury/agents/context_providers/subagent.py +46 -0
- aury/agents/context_providers/tool.py +68 -0
- aury/agents/core/__init__.py +83 -0
- aury/agents/core/base.py +573 -0
- aury/agents/core/context.py +797 -0
- aury/agents/core/context_builder.py +303 -0
- aury/agents/core/event_bus/__init__.py +15 -0
- aury/agents/core/event_bus/bus.py +203 -0
- aury/agents/core/factory.py +169 -0
- aury/agents/core/isolator.py +97 -0
- aury/agents/core/logging.py +95 -0
- aury/agents/core/parallel.py +194 -0
- aury/agents/core/runner.py +139 -0
- aury/agents/core/services/__init__.py +5 -0
- aury/agents/core/services/file_session.py +144 -0
- aury/agents/core/services/message.py +53 -0
- aury/agents/core/services/session.py +53 -0
- aury/agents/core/signals.py +109 -0
- aury/agents/core/state.py +363 -0
- aury/agents/core/types/__init__.py +107 -0
- aury/agents/core/types/action.py +176 -0
- aury/agents/core/types/artifact.py +135 -0
- aury/agents/core/types/block.py +736 -0
- aury/agents/core/types/message.py +350 -0
- aury/agents/core/types/recall.py +144 -0
- aury/agents/core/types/session.py +257 -0
- aury/agents/core/types/subagent.py +154 -0
- aury/agents/core/types/tool.py +205 -0
- aury/agents/eval/__init__.py +331 -0
- aury/agents/hitl/__init__.py +57 -0
- aury/agents/hitl/ask_user.py +242 -0
- aury/agents/hitl/compaction.py +230 -0
- aury/agents/hitl/exceptions.py +87 -0
- aury/agents/hitl/permission.py +617 -0
- aury/agents/hitl/revert.py +216 -0
- aury/agents/llm/__init__.py +31 -0
- aury/agents/llm/adapter.py +367 -0
- aury/agents/llm/openai.py +294 -0
- aury/agents/llm/provider.py +476 -0
- aury/agents/mcp/__init__.py +153 -0
- aury/agents/memory/__init__.py +46 -0
- aury/agents/memory/compaction.py +394 -0
- aury/agents/memory/manager.py +465 -0
- aury/agents/memory/processor.py +177 -0
- aury/agents/memory/store.py +187 -0
- aury/agents/memory/types.py +137 -0
- aury/agents/messages/__init__.py +40 -0
- aury/agents/messages/config.py +47 -0
- aury/agents/messages/raw_store.py +224 -0
- aury/agents/messages/store.py +118 -0
- aury/agents/messages/types.py +88 -0
- aury/agents/middleware/__init__.py +31 -0
- aury/agents/middleware/base.py +341 -0
- aury/agents/middleware/chain.py +342 -0
- aury/agents/middleware/message.py +129 -0
- aury/agents/middleware/message_container.py +126 -0
- aury/agents/middleware/raw_message.py +153 -0
- aury/agents/middleware/truncation.py +139 -0
- aury/agents/middleware/types.py +81 -0
- aury/agents/plugin.py +162 -0
- aury/agents/react/__init__.py +4 -0
- aury/agents/react/agent.py +1923 -0
- aury/agents/sandbox/__init__.py +23 -0
- aury/agents/sandbox/local.py +239 -0
- aury/agents/sandbox/remote.py +200 -0
- aury/agents/sandbox/types.py +115 -0
- aury/agents/skill/__init__.py +16 -0
- aury/agents/skill/loader.py +180 -0
- aury/agents/skill/types.py +83 -0
- aury/agents/tool/__init__.py +39 -0
- aury/agents/tool/builtin/__init__.py +23 -0
- aury/agents/tool/builtin/ask_user.py +155 -0
- aury/agents/tool/builtin/bash.py +107 -0
- aury/agents/tool/builtin/delegate.py +726 -0
- aury/agents/tool/builtin/edit.py +121 -0
- aury/agents/tool/builtin/plan.py +277 -0
- aury/agents/tool/builtin/read.py +91 -0
- aury/agents/tool/builtin/thinking.py +111 -0
- aury/agents/tool/builtin/yield_result.py +130 -0
- aury/agents/tool/decorator.py +252 -0
- aury/agents/tool/set.py +204 -0
- aury/agents/usage/__init__.py +12 -0
- aury/agents/usage/tracker.py +236 -0
- aury/agents/workflow/__init__.py +85 -0
- aury/agents/workflow/adapter.py +268 -0
- aury/agents/workflow/dag.py +116 -0
- aury/agents/workflow/dsl.py +575 -0
- aury/agents/workflow/executor.py +659 -0
- aury/agents/workflow/expression.py +136 -0
- aury/agents/workflow/parser.py +182 -0
- aury/agents/workflow/state.py +145 -0
- aury/agents/workflow/types.py +86 -0
- aury_agent-0.0.4.dist-info/METADATA +90 -0
- aury_agent-0.0.4.dist-info/RECORD +149 -0
- aury_agent-0.0.4.dist-info/WHEEL +4 -0
- aury_agent-0.0.4.dist-info/entry_points.txt +2 -0
aury/__init__.py
ADDED
aury/agents/__init__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Aury Agent Framework.
|
|
2
|
+
|
|
3
|
+
A framework supporting both React Agent (autonomous loop) and Workflow (DAG orchestration).
|
|
4
|
+
|
|
5
|
+
Package Structure:
|
|
6
|
+
aury.agents.backends - Backend protocols and implementations
|
|
7
|
+
aury.agents.core - Core infrastructure (types, bus, context)
|
|
8
|
+
aury.agents.llm - LLM adapters
|
|
9
|
+
aury.agents.plugin - Middleware system
|
|
10
|
+
aury.agents.memory - Memory system
|
|
11
|
+
aury.agents.react - ReactAgent
|
|
12
|
+
aury.agents.workflow - Workflow orchestration
|
|
13
|
+
aury.agents.tool - Tool system
|
|
14
|
+
aury.agents.skill - Skill system (capability bundles)
|
|
15
|
+
aury.agents.sandbox - Sandbox system (isolated execution)
|
|
16
|
+
aury.agents.cli - CLI
|
|
17
|
+
|
|
18
|
+
Quick Start:
|
|
19
|
+
from aury.agents import ReactAgent, AgentConfig
|
|
20
|
+
from aury.agents.core.types import Session, PromptInput
|
|
21
|
+
from aury.agents.backends import MemoryStateBackend
|
|
22
|
+
from aury.agents.core.event_bus import EventBus, Bus
|
|
23
|
+
from aury.agents.llm import MockLLMProvider
|
|
24
|
+
from aury.agents.tool import ToolSet
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
|
|
29
|
+
# Only export the most commonly used classes at top level
|
|
30
|
+
# For other classes, import from submodules directly
|
|
31
|
+
from .core.base import BaseAgent, AgentConfig, ToolInjectionMode
|
|
32
|
+
from .core.event_bus import EventBus, Events
|
|
33
|
+
from .core.context import InvocationContext
|
|
34
|
+
from .core.types import Session, PromptInput, generate_id
|
|
35
|
+
from .react import ReactAgent
|
|
36
|
+
from .workflow import WorkflowAgent
|
|
37
|
+
from .context_providers import ContextProvider, AgentContext
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"__version__",
|
|
41
|
+
"BaseAgent",
|
|
42
|
+
"AgentConfig",
|
|
43
|
+
"ToolInjectionMode",
|
|
44
|
+
"EventBus",
|
|
45
|
+
"Events",
|
|
46
|
+
"InvocationContext",
|
|
47
|
+
"Session",
|
|
48
|
+
"PromptInput",
|
|
49
|
+
"generate_id",
|
|
50
|
+
"ReactAgent",
|
|
51
|
+
"WorkflowAgent",
|
|
52
|
+
# Providers
|
|
53
|
+
"ContextProvider",
|
|
54
|
+
"AgentContext",
|
|
55
|
+
]
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""A2A (Agent-to-Agent) communication module.
|
|
2
|
+
|
|
3
|
+
TODO: Implement A2A protocol support for inter-agent communication.
|
|
4
|
+
|
|
5
|
+
This module will provide:
|
|
6
|
+
- A2AClient: Client for calling remote A2A-compliant agents
|
|
7
|
+
- A2AServer: Server to expose agent as A2A endpoint
|
|
8
|
+
- AgentCard: Agent capability declaration
|
|
9
|
+
- AgentSkill: Skill declaration for AgentCard
|
|
10
|
+
|
|
11
|
+
Reference: Google A2A Protocol
|
|
12
|
+
https://github.com/google-a2a/a2a-samples
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import Any, AsyncIterator
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# =============================================================================
|
|
21
|
+
# TODO: Agent Card & Skill
|
|
22
|
+
# =============================================================================
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class AgentSkill:
|
|
26
|
+
"""Skill declaration for AgentCard.
|
|
27
|
+
|
|
28
|
+
TODO: Implement skill definition.
|
|
29
|
+
"""
|
|
30
|
+
id: str
|
|
31
|
+
name: str
|
|
32
|
+
description: str
|
|
33
|
+
input_modes: list[str] = field(default_factory=lambda: ["text"])
|
|
34
|
+
output_modes: list[str] = field(default_factory=lambda: ["text"])
|
|
35
|
+
examples: list[str] = field(default_factory=list)
|
|
36
|
+
tags: list[str] = field(default_factory=list)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class AgentCard:
|
|
41
|
+
"""Agent capability declaration.
|
|
42
|
+
|
|
43
|
+
TODO: Implement agent card for A2A discovery.
|
|
44
|
+
"""
|
|
45
|
+
name: str
|
|
46
|
+
description: str
|
|
47
|
+
url: str = ""
|
|
48
|
+
version: str = "1.0.0"
|
|
49
|
+
capabilities: dict[str, Any] = field(default_factory=dict)
|
|
50
|
+
skills: list[AgentSkill] = field(default_factory=list)
|
|
51
|
+
default_input_modes: list[str] = field(default_factory=lambda: ["text"])
|
|
52
|
+
default_output_modes: list[str] = field(default_factory=lambda: ["text"])
|
|
53
|
+
authentication: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# =============================================================================
|
|
57
|
+
# TODO: A2A Client
|
|
58
|
+
# =============================================================================
|
|
59
|
+
|
|
60
|
+
class A2AClient:
|
|
61
|
+
"""Client for calling remote A2A-compliant agents.
|
|
62
|
+
|
|
63
|
+
TODO: Implement A2A client.
|
|
64
|
+
|
|
65
|
+
Usage:
|
|
66
|
+
# Discover agent
|
|
67
|
+
agent_card = await A2AClient.discover("http://example.com")
|
|
68
|
+
|
|
69
|
+
# Send task
|
|
70
|
+
client = A2AClient(agent_card)
|
|
71
|
+
result = await client.send_task("Hello")
|
|
72
|
+
|
|
73
|
+
# Stream task
|
|
74
|
+
async for event in client.stream_task("Generate report"):
|
|
75
|
+
print(event)
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, agent_card: AgentCard):
|
|
79
|
+
self.agent_card = agent_card
|
|
80
|
+
raise NotImplementedError("A2A client is not yet implemented")
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
async def discover(cls, url: str) -> AgentCard:
|
|
84
|
+
"""Discover agent capabilities from URL.
|
|
85
|
+
|
|
86
|
+
TODO: Implement discovery via /.well-known/agent.json
|
|
87
|
+
"""
|
|
88
|
+
raise NotImplementedError("A2A discovery is not yet implemented")
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def from_url(cls, url: str) -> "A2AClient":
|
|
92
|
+
"""Create client from URL (discovers agent card first)."""
|
|
93
|
+
raise NotImplementedError("A2A client is not yet implemented")
|
|
94
|
+
|
|
95
|
+
async def send_task(
|
|
96
|
+
self,
|
|
97
|
+
message: str,
|
|
98
|
+
accepted_output_modes: list[str] | None = None,
|
|
99
|
+
) -> Any:
|
|
100
|
+
"""Send task and wait for result.
|
|
101
|
+
|
|
102
|
+
TODO: Implement synchronous task submission.
|
|
103
|
+
"""
|
|
104
|
+
raise NotImplementedError("A2A send_task is not yet implemented")
|
|
105
|
+
|
|
106
|
+
async def stream_task(
|
|
107
|
+
self,
|
|
108
|
+
message: str,
|
|
109
|
+
accepted_output_modes: list[str] | None = None,
|
|
110
|
+
) -> AsyncIterator[Any]:
|
|
111
|
+
"""Send task and stream results.
|
|
112
|
+
|
|
113
|
+
TODO: Implement streaming task submission via SSE.
|
|
114
|
+
"""
|
|
115
|
+
raise NotImplementedError("A2A stream_task is not yet implemented")
|
|
116
|
+
yield # Make it a generator
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# =============================================================================
|
|
120
|
+
# TODO: A2A Server
|
|
121
|
+
# =============================================================================
|
|
122
|
+
|
|
123
|
+
class A2AServer:
|
|
124
|
+
"""Server to expose agent as A2A endpoint.
|
|
125
|
+
|
|
126
|
+
TODO: Implement A2A server.
|
|
127
|
+
|
|
128
|
+
Usage:
|
|
129
|
+
server = A2AServer(
|
|
130
|
+
agent=my_agent,
|
|
131
|
+
agent_card=agent_card,
|
|
132
|
+
host="0.0.0.0",
|
|
133
|
+
port=8080,
|
|
134
|
+
)
|
|
135
|
+
await server.start()
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self,
|
|
140
|
+
agent: Any,
|
|
141
|
+
agent_card: AgentCard,
|
|
142
|
+
host: str = "0.0.0.0",
|
|
143
|
+
port: int = 8080,
|
|
144
|
+
):
|
|
145
|
+
self.agent = agent
|
|
146
|
+
self.agent_card = agent_card
|
|
147
|
+
self.host = host
|
|
148
|
+
self.port = port
|
|
149
|
+
raise NotImplementedError("A2A server is not yet implemented")
|
|
150
|
+
|
|
151
|
+
async def start(self) -> None:
|
|
152
|
+
"""Start the A2A server.
|
|
153
|
+
|
|
154
|
+
TODO: Implement HTTP server with A2A protocol handlers.
|
|
155
|
+
"""
|
|
156
|
+
raise NotImplementedError("A2A server is not yet implemented")
|
|
157
|
+
|
|
158
|
+
async def stop(self) -> None:
|
|
159
|
+
"""Stop the A2A server."""
|
|
160
|
+
raise NotImplementedError("A2A server is not yet implemented")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
__all__ = [
|
|
164
|
+
"AgentSkill",
|
|
165
|
+
"AgentCard",
|
|
166
|
+
"A2AClient",
|
|
167
|
+
"A2AServer",
|
|
168
|
+
]
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Backend protocols and implementations.
|
|
2
|
+
|
|
3
|
+
Backends provide abstracted interfaces for various capabilities:
|
|
4
|
+
|
|
5
|
+
Data Backends (storage):
|
|
6
|
+
- SessionBackend: Session management
|
|
7
|
+
- InvocationBackend: Invocation management
|
|
8
|
+
- MessageBackend: Message storage (truncated/raw)
|
|
9
|
+
- MemoryBackend: Long-term memory with search
|
|
10
|
+
- ArtifactBackend: File/artifact storage
|
|
11
|
+
- StateBackend: Generic key-value state
|
|
12
|
+
|
|
13
|
+
Capability Backends:
|
|
14
|
+
- SnapshotBackend: File state tracking and revert
|
|
15
|
+
- ShellBackend: Shell command execution
|
|
16
|
+
- FileBackend: File system operations
|
|
17
|
+
- CodeBackend: Code execution
|
|
18
|
+
- SubAgentBackend: Sub-agent registry and retrieval
|
|
19
|
+
|
|
20
|
+
Backends Container:
|
|
21
|
+
- Backends: Dataclass container for dependency injection
|
|
22
|
+
"""
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from typing import TYPE_CHECKING
|
|
25
|
+
|
|
26
|
+
# Data backends - new architecture
|
|
27
|
+
from .session import SessionBackend, InMemorySessionBackend
|
|
28
|
+
from .invocation import InvocationBackend, InMemoryInvocationBackend
|
|
29
|
+
from .message import MessageBackend, MessageType, InMemoryMessageBackend
|
|
30
|
+
from .memory import MemoryBackend, InMemoryMemoryBackend
|
|
31
|
+
from .artifact import ArtifactBackend, ArtifactSource, InMemoryArtifactBackend
|
|
32
|
+
|
|
33
|
+
# State backend - simplified to key-value
|
|
34
|
+
from .state import StateBackend, StateStore, StoreBasedStateBackend, SQLiteStateBackend, MemoryStateBackend, FileStateBackend, CompositeStateBackend
|
|
35
|
+
|
|
36
|
+
# Capability backends - existing
|
|
37
|
+
from .snapshot import SnapshotBackend, Patch, InMemorySnapshotBackend, GitSnapshotBackend, GitS3HybridBackend
|
|
38
|
+
from .shell import ShellBackend, ShellResult, LocalShellBackend
|
|
39
|
+
from .file import FileBackend, LocalFileBackend
|
|
40
|
+
from .code import CodeBackend, CodeResult
|
|
41
|
+
from .subagent import SubAgentBackend, AgentConfig, ListSubAgentBackend
|
|
42
|
+
from .sandbox import SandboxShellBackend, SandboxCodeBackend
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Backends:
|
|
47
|
+
"""Container for all backends.
|
|
48
|
+
|
|
49
|
+
Provides a unified way to inject backends into agents and context.
|
|
50
|
+
|
|
51
|
+
Data backends (for storage):
|
|
52
|
+
- session: Session management (required)
|
|
53
|
+
- invocation: Invocation management (required)
|
|
54
|
+
- message: Message storage (required)
|
|
55
|
+
- memory: Long-term memory (optional)
|
|
56
|
+
- artifact: Artifact storage (optional)
|
|
57
|
+
- state: Generic key-value state (optional)
|
|
58
|
+
|
|
59
|
+
Capability backends (for actions):
|
|
60
|
+
- snapshot: File tracking and revert (optional)
|
|
61
|
+
- shell: Shell execution (optional)
|
|
62
|
+
- file: File operations (optional)
|
|
63
|
+
- code: Code execution (optional)
|
|
64
|
+
- subagent: Sub-agent registry (optional)
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
# Create with defaults
|
|
68
|
+
backends = Backends.create_default()
|
|
69
|
+
|
|
70
|
+
# Create with custom implementations
|
|
71
|
+
backends = Backends(
|
|
72
|
+
session=DatabaseSessionBackend(db),
|
|
73
|
+
invocation=DatabaseInvocationBackend(db),
|
|
74
|
+
message=DatabaseMessageBackend(db),
|
|
75
|
+
memory=VectorMemoryBackend(embedding_model),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Use in agent
|
|
79
|
+
agent = ReactAgent.create(llm=llm, backends=backends)
|
|
80
|
+
"""
|
|
81
|
+
# Data backends - required
|
|
82
|
+
session: SessionBackend
|
|
83
|
+
invocation: InvocationBackend
|
|
84
|
+
message: MessageBackend
|
|
85
|
+
|
|
86
|
+
# Data backends - optional
|
|
87
|
+
memory: MemoryBackend | None = None
|
|
88
|
+
artifact: ArtifactBackend | None = None
|
|
89
|
+
state: StateBackend | None = None
|
|
90
|
+
|
|
91
|
+
# Capability backends - optional
|
|
92
|
+
snapshot: SnapshotBackend | None = None
|
|
93
|
+
shell: ShellBackend | None = None
|
|
94
|
+
file: FileBackend | None = None
|
|
95
|
+
code: CodeBackend | None = None
|
|
96
|
+
subagent: SubAgentBackend | None = None
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def create_default(cls) -> "Backends":
|
|
100
|
+
"""Create Backends with default in-memory implementations.
|
|
101
|
+
|
|
102
|
+
Suitable for testing and simple single-process use cases.
|
|
103
|
+
"""
|
|
104
|
+
return cls(
|
|
105
|
+
session=InMemorySessionBackend(),
|
|
106
|
+
invocation=InMemoryInvocationBackend(),
|
|
107
|
+
message=InMemoryMessageBackend(),
|
|
108
|
+
memory=InMemoryMemoryBackend(),
|
|
109
|
+
artifact=InMemoryArtifactBackend(),
|
|
110
|
+
state=MemoryStateBackend(),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def create_sqlite(cls, db_path: str = "./data/agent.db") -> "Backends":
|
|
115
|
+
"""Create Backends with SQLite storage.
|
|
116
|
+
|
|
117
|
+
Uses SQLite for state backend, in-memory for others.
|
|
118
|
+
For production, implement database backends.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
db_path: Path to SQLite database file
|
|
122
|
+
"""
|
|
123
|
+
return cls(
|
|
124
|
+
session=InMemorySessionBackend(),
|
|
125
|
+
invocation=InMemoryInvocationBackend(),
|
|
126
|
+
message=InMemoryMessageBackend(),
|
|
127
|
+
memory=InMemoryMemoryBackend(),
|
|
128
|
+
artifact=InMemoryArtifactBackend(),
|
|
129
|
+
state=SQLiteStateBackend(db_path),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
__all__ = [
|
|
134
|
+
# Backends container
|
|
135
|
+
"Backends",
|
|
136
|
+
|
|
137
|
+
# Session backend
|
|
138
|
+
"SessionBackend",
|
|
139
|
+
"InMemorySessionBackend",
|
|
140
|
+
|
|
141
|
+
# Invocation backend
|
|
142
|
+
"InvocationBackend",
|
|
143
|
+
"InMemoryInvocationBackend",
|
|
144
|
+
|
|
145
|
+
# Message backend
|
|
146
|
+
"MessageBackend",
|
|
147
|
+
"MessageType",
|
|
148
|
+
"InMemoryMessageBackend",
|
|
149
|
+
|
|
150
|
+
# Memory backend
|
|
151
|
+
"MemoryBackend",
|
|
152
|
+
"InMemoryMemoryBackend",
|
|
153
|
+
|
|
154
|
+
# Artifact backend
|
|
155
|
+
"ArtifactBackend",
|
|
156
|
+
"ArtifactSource",
|
|
157
|
+
"InMemoryArtifactBackend",
|
|
158
|
+
|
|
159
|
+
# State backend (key-value)
|
|
160
|
+
"StateBackend",
|
|
161
|
+
"StateStore",
|
|
162
|
+
"StoreBasedStateBackend",
|
|
163
|
+
"SQLiteStateBackend",
|
|
164
|
+
"MemoryStateBackend",
|
|
165
|
+
"FileStateBackend",
|
|
166
|
+
"CompositeStateBackend",
|
|
167
|
+
|
|
168
|
+
# Snapshot backend
|
|
169
|
+
"SnapshotBackend",
|
|
170
|
+
"Patch",
|
|
171
|
+
"InMemorySnapshotBackend",
|
|
172
|
+
"GitSnapshotBackend",
|
|
173
|
+
"GitS3HybridBackend",
|
|
174
|
+
|
|
175
|
+
# Shell backend
|
|
176
|
+
"ShellBackend",
|
|
177
|
+
"ShellResult",
|
|
178
|
+
"LocalShellBackend",
|
|
179
|
+
|
|
180
|
+
# File backend
|
|
181
|
+
"FileBackend",
|
|
182
|
+
"LocalFileBackend",
|
|
183
|
+
|
|
184
|
+
# Code backend
|
|
185
|
+
"CodeBackend",
|
|
186
|
+
"CodeResult",
|
|
187
|
+
|
|
188
|
+
# SubAgent backend
|
|
189
|
+
"SubAgentBackend",
|
|
190
|
+
"AgentConfig",
|
|
191
|
+
"ListSubAgentBackend",
|
|
192
|
+
|
|
193
|
+
# Sandbox backends
|
|
194
|
+
"SandboxShellBackend",
|
|
195
|
+
"SandboxCodeBackend",
|
|
196
|
+
]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""In-memory artifact backend implementation."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .types import ArtifactSource
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InMemoryArtifactBackend:
|
|
12
|
+
"""In-memory implementation of ArtifactBackend.
|
|
13
|
+
|
|
14
|
+
Stores artifact metadata in memory.
|
|
15
|
+
Suitable for testing and simple use cases.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
self._artifacts: dict[str, dict[str, Any]] = {}
|
|
20
|
+
self._session_artifacts: dict[str, list[str]] = {}
|
|
21
|
+
|
|
22
|
+
def _make_key(self, session_id: str, namespace: str | None) -> str:
|
|
23
|
+
if namespace:
|
|
24
|
+
return f"{session_id}:{namespace}"
|
|
25
|
+
return session_id
|
|
26
|
+
|
|
27
|
+
async def save(
|
|
28
|
+
self,
|
|
29
|
+
session_id: str,
|
|
30
|
+
name: str,
|
|
31
|
+
path: str,
|
|
32
|
+
source: ArtifactSource | str | None = None,
|
|
33
|
+
mime_type: str | None = None,
|
|
34
|
+
size: int | None = None,
|
|
35
|
+
invocation_id: str | None = None,
|
|
36
|
+
namespace: str | None = None,
|
|
37
|
+
metadata: dict[str, Any] | None = None,
|
|
38
|
+
) -> str:
|
|
39
|
+
"""Save an artifact reference."""
|
|
40
|
+
artifact_id = f"art_{uuid.uuid4().hex[:12]}"
|
|
41
|
+
key = self._make_key(session_id, namespace)
|
|
42
|
+
|
|
43
|
+
artifact = {
|
|
44
|
+
"id": artifact_id,
|
|
45
|
+
"session_id": session_id,
|
|
46
|
+
"name": name,
|
|
47
|
+
"path": path,
|
|
48
|
+
"source": source,
|
|
49
|
+
"mime_type": mime_type,
|
|
50
|
+
"size": size,
|
|
51
|
+
"invocation_id": invocation_id,
|
|
52
|
+
"namespace": namespace,
|
|
53
|
+
"metadata": metadata or {},
|
|
54
|
+
"created_at": datetime.now().isoformat(),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
self._artifacts[artifact_id] = artifact
|
|
58
|
+
|
|
59
|
+
if key not in self._session_artifacts:
|
|
60
|
+
self._session_artifacts[key] = []
|
|
61
|
+
self._session_artifacts[key].append(artifact_id)
|
|
62
|
+
|
|
63
|
+
return artifact_id
|
|
64
|
+
|
|
65
|
+
async def get(self, id: str) -> dict[str, Any] | None:
|
|
66
|
+
"""Get artifact by ID."""
|
|
67
|
+
return self._artifacts.get(id)
|
|
68
|
+
|
|
69
|
+
async def delete(self, id: str) -> bool:
|
|
70
|
+
"""Delete an artifact reference."""
|
|
71
|
+
if id not in self._artifacts:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
artifact = self._artifacts.pop(id)
|
|
75
|
+
session_id = artifact["session_id"]
|
|
76
|
+
namespace = artifact.get("namespace")
|
|
77
|
+
key = self._make_key(session_id, namespace)
|
|
78
|
+
|
|
79
|
+
if key in self._session_artifacts:
|
|
80
|
+
self._session_artifacts[key] = [
|
|
81
|
+
aid for aid in self._session_artifacts[key] if aid != id
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
async def delete_by_invocation(
|
|
87
|
+
self,
|
|
88
|
+
session_id: str,
|
|
89
|
+
invocation_id: str,
|
|
90
|
+
namespace: str | None = None,
|
|
91
|
+
) -> int:
|
|
92
|
+
"""Delete artifacts by invocation."""
|
|
93
|
+
key = self._make_key(session_id, namespace)
|
|
94
|
+
artifact_ids = self._session_artifacts.get(key, [])
|
|
95
|
+
|
|
96
|
+
to_delete = [
|
|
97
|
+
aid for aid in artifact_ids
|
|
98
|
+
if self._artifacts.get(aid, {}).get("invocation_id") == invocation_id
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
for aid in to_delete:
|
|
102
|
+
await self.delete(aid)
|
|
103
|
+
|
|
104
|
+
return len(to_delete)
|
|
105
|
+
|
|
106
|
+
async def list_by_session(
|
|
107
|
+
self,
|
|
108
|
+
session_id: str,
|
|
109
|
+
namespace: str | None = None,
|
|
110
|
+
source: ArtifactSource | str | None = None,
|
|
111
|
+
limit: int = 100,
|
|
112
|
+
) -> list[dict[str, Any]]:
|
|
113
|
+
"""List artifacts for a session."""
|
|
114
|
+
key = self._make_key(session_id, namespace)
|
|
115
|
+
artifact_ids = self._session_artifacts.get(key, [])
|
|
116
|
+
|
|
117
|
+
results = []
|
|
118
|
+
for aid in artifact_ids:
|
|
119
|
+
artifact = self._artifacts.get(aid)
|
|
120
|
+
if artifact:
|
|
121
|
+
# Filter by source if specified
|
|
122
|
+
if source and artifact.get("source") != source:
|
|
123
|
+
continue
|
|
124
|
+
results.append(artifact)
|
|
125
|
+
|
|
126
|
+
results.sort(key=lambda x: x.get("created_at", ""), reverse=True)
|
|
127
|
+
return results[:limit]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
__all__ = ["InMemoryArtifactBackend"]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Artifact backend types and protocols."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any, Literal, Protocol, runtime_checkable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
ArtifactSource = Literal["tool", "agent", "user", "system"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@runtime_checkable
|
|
11
|
+
class ArtifactBackend(Protocol):
|
|
12
|
+
"""Protocol for artifact storage.
|
|
13
|
+
|
|
14
|
+
Artifacts are file references produced during agent execution,
|
|
15
|
+
such as generated images, documents, code files, etc.
|
|
16
|
+
|
|
17
|
+
Note: This stores file metadata and path references, not binary data.
|
|
18
|
+
Actual file storage is handled by the file system or object storage.
|
|
19
|
+
|
|
20
|
+
Example usage:
|
|
21
|
+
# Save artifact reference
|
|
22
|
+
artifact_id = await backend.save(
|
|
23
|
+
session_id="sess_123",
|
|
24
|
+
name="report.pdf",
|
|
25
|
+
path="/uploads/sess_123/report.pdf",
|
|
26
|
+
source="tool",
|
|
27
|
+
mime_type="application/pdf",
|
|
28
|
+
invocation_id="inv_456",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Get artifact metadata
|
|
32
|
+
artifact = await backend.get(artifact_id)
|
|
33
|
+
|
|
34
|
+
# List artifacts
|
|
35
|
+
artifacts = await backend.list_by_session("sess_123")
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
async def save(
|
|
39
|
+
self,
|
|
40
|
+
session_id: str,
|
|
41
|
+
name: str,
|
|
42
|
+
path: str,
|
|
43
|
+
source: ArtifactSource | str | None = None,
|
|
44
|
+
mime_type: str | None = None,
|
|
45
|
+
size: int | None = None,
|
|
46
|
+
invocation_id: str | None = None,
|
|
47
|
+
namespace: str | None = None,
|
|
48
|
+
metadata: dict[str, Any] | None = None,
|
|
49
|
+
) -> str:
|
|
50
|
+
"""Save an artifact reference.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
session_id: Session ID
|
|
54
|
+
name: Display name / filename
|
|
55
|
+
path: File path or URL (storage location)
|
|
56
|
+
source: Origin of artifact (tool, agent, user, system)
|
|
57
|
+
mime_type: MIME type (e.g., "image/png")
|
|
58
|
+
size: File size in bytes
|
|
59
|
+
invocation_id: Optional invocation ID for grouping
|
|
60
|
+
namespace: Optional namespace for isolation
|
|
61
|
+
metadata: Optional additional metadata
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Generated artifact ID
|
|
65
|
+
"""
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
async def get(self, id: str) -> dict[str, Any] | None:
|
|
69
|
+
"""Get artifact by ID.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
id: Artifact ID
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Artifact dict or None if not found:
|
|
76
|
+
{"id": str, "name": str, "path": str, "source": str, ...}
|
|
77
|
+
"""
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
async def delete(self, id: str) -> bool:
|
|
81
|
+
"""Delete an artifact reference.
|
|
82
|
+
|
|
83
|
+
Note: This only deletes the metadata, not the actual file.
|
|
84
|
+
File cleanup should be handled separately.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
id: Artifact ID
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
True if deleted, False if not found
|
|
91
|
+
"""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
async def delete_by_invocation(
|
|
95
|
+
self,
|
|
96
|
+
session_id: str,
|
|
97
|
+
invocation_id: str,
|
|
98
|
+
namespace: str | None = None,
|
|
99
|
+
) -> int:
|
|
100
|
+
"""Delete artifacts by invocation (for revert).
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
session_id: Session ID
|
|
104
|
+
invocation_id: Invocation ID to delete
|
|
105
|
+
namespace: Optional namespace filter
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Number of artifacts deleted
|
|
109
|
+
"""
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
async def list_by_session(
|
|
113
|
+
self,
|
|
114
|
+
session_id: str,
|
|
115
|
+
namespace: str | None = None,
|
|
116
|
+
source: ArtifactSource | str | None = None,
|
|
117
|
+
limit: int = 100,
|
|
118
|
+
) -> list[dict[str, Any]]:
|
|
119
|
+
"""List artifacts for a session.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
session_id: Session ID
|
|
123
|
+
namespace: Optional namespace filter
|
|
124
|
+
source: Optional filter by source
|
|
125
|
+
limit: Max results
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
List of artifact dicts
|
|
129
|
+
"""
|
|
130
|
+
...
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
__all__ = ["ArtifactBackend", "ArtifactSource"]
|