AbstractRuntime 0.2.0__py3-none-any.whl → 0.4.1__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.
- abstractruntime/__init__.py +83 -3
- abstractruntime/core/config.py +82 -2
- abstractruntime/core/event_keys.py +62 -0
- abstractruntime/core/models.py +17 -1
- abstractruntime/core/policy.py +74 -3
- abstractruntime/core/runtime.py +3334 -28
- abstractruntime/core/vars.py +103 -2
- abstractruntime/evidence/__init__.py +10 -0
- abstractruntime/evidence/recorder.py +325 -0
- abstractruntime/history_bundle.py +772 -0
- abstractruntime/integrations/abstractcore/__init__.py +6 -0
- abstractruntime/integrations/abstractcore/constants.py +19 -0
- abstractruntime/integrations/abstractcore/default_tools.py +258 -0
- abstractruntime/integrations/abstractcore/effect_handlers.py +2622 -32
- abstractruntime/integrations/abstractcore/embeddings_client.py +69 -0
- abstractruntime/integrations/abstractcore/factory.py +149 -16
- abstractruntime/integrations/abstractcore/llm_client.py +891 -55
- abstractruntime/integrations/abstractcore/mcp_worker.py +587 -0
- abstractruntime/integrations/abstractcore/observability.py +80 -0
- abstractruntime/integrations/abstractcore/session_attachments.py +946 -0
- abstractruntime/integrations/abstractcore/summarizer.py +154 -0
- abstractruntime/integrations/abstractcore/tool_executor.py +509 -31
- abstractruntime/integrations/abstractcore/workspace_scoped_tools.py +561 -0
- abstractruntime/integrations/abstractmemory/__init__.py +3 -0
- abstractruntime/integrations/abstractmemory/effect_handlers.py +946 -0
- abstractruntime/memory/__init__.py +21 -0
- abstractruntime/memory/active_context.py +751 -0
- abstractruntime/memory/active_memory.py +452 -0
- abstractruntime/memory/compaction.py +105 -0
- abstractruntime/memory/kg_packets.py +164 -0
- abstractruntime/memory/memact_composer.py +175 -0
- abstractruntime/memory/recall_levels.py +163 -0
- abstractruntime/memory/token_budget.py +86 -0
- abstractruntime/rendering/__init__.py +17 -0
- abstractruntime/rendering/agent_trace_report.py +256 -0
- abstractruntime/rendering/json_stringify.py +136 -0
- abstractruntime/scheduler/scheduler.py +93 -2
- abstractruntime/storage/__init__.py +7 -2
- abstractruntime/storage/artifacts.py +175 -32
- abstractruntime/storage/base.py +17 -1
- abstractruntime/storage/commands.py +339 -0
- abstractruntime/storage/in_memory.py +41 -1
- abstractruntime/storage/json_files.py +210 -14
- abstractruntime/storage/observable.py +136 -0
- abstractruntime/storage/offloading.py +433 -0
- abstractruntime/storage/sqlite.py +836 -0
- abstractruntime/visualflow_compiler/__init__.py +29 -0
- abstractruntime/visualflow_compiler/adapters/__init__.py +11 -0
- abstractruntime/visualflow_compiler/adapters/agent_adapter.py +126 -0
- abstractruntime/visualflow_compiler/adapters/context_adapter.py +109 -0
- abstractruntime/visualflow_compiler/adapters/control_adapter.py +615 -0
- abstractruntime/visualflow_compiler/adapters/effect_adapter.py +1051 -0
- abstractruntime/visualflow_compiler/adapters/event_adapter.py +307 -0
- abstractruntime/visualflow_compiler/adapters/function_adapter.py +97 -0
- abstractruntime/visualflow_compiler/adapters/memact_adapter.py +114 -0
- abstractruntime/visualflow_compiler/adapters/subflow_adapter.py +74 -0
- abstractruntime/visualflow_compiler/adapters/variable_adapter.py +316 -0
- abstractruntime/visualflow_compiler/compiler.py +3832 -0
- abstractruntime/visualflow_compiler/flow.py +247 -0
- abstractruntime/visualflow_compiler/visual/__init__.py +13 -0
- abstractruntime/visualflow_compiler/visual/agent_ids.py +29 -0
- abstractruntime/visualflow_compiler/visual/builtins.py +1376 -0
- abstractruntime/visualflow_compiler/visual/code_executor.py +214 -0
- abstractruntime/visualflow_compiler/visual/executor.py +2804 -0
- abstractruntime/visualflow_compiler/visual/models.py +211 -0
- abstractruntime/workflow_bundle/__init__.py +52 -0
- abstractruntime/workflow_bundle/models.py +236 -0
- abstractruntime/workflow_bundle/packer.py +317 -0
- abstractruntime/workflow_bundle/reader.py +87 -0
- abstractruntime/workflow_bundle/registry.py +587 -0
- abstractruntime-0.4.1.dist-info/METADATA +177 -0
- abstractruntime-0.4.1.dist-info/RECORD +86 -0
- abstractruntime-0.4.1.dist-info/entry_points.txt +2 -0
- abstractruntime-0.2.0.dist-info/METADATA +0 -163
- abstractruntime-0.2.0.dist-info/RECORD +0 -32
- {abstractruntime-0.2.0.dist-info → abstractruntime-0.4.1.dist-info}/WHEEL +0 -0
- {abstractruntime-0.2.0.dist-info → abstractruntime-0.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,6 +18,7 @@ from .llm_client import (
|
|
|
18
18
|
LocalAbstractCoreLLMClient,
|
|
19
19
|
RemoteAbstractCoreLLMClient,
|
|
20
20
|
)
|
|
21
|
+
from .embeddings_client import AbstractCoreEmbeddingsClient, EmbeddingsResult
|
|
21
22
|
from .tool_executor import AbstractCoreToolExecutor, MappingToolExecutor, PassthroughToolExecutor, ToolExecutor
|
|
22
23
|
from .effect_handlers import build_effect_handlers
|
|
23
24
|
from .factory import (
|
|
@@ -27,11 +28,14 @@ from .factory import (
|
|
|
27
28
|
create_remote_file_runtime,
|
|
28
29
|
create_remote_runtime,
|
|
29
30
|
)
|
|
31
|
+
from .observability import attach_global_event_bus_bridge, emit_step_record
|
|
30
32
|
|
|
31
33
|
__all__ = [
|
|
32
34
|
"AbstractCoreLLMClient",
|
|
33
35
|
"LocalAbstractCoreLLMClient",
|
|
34
36
|
"RemoteAbstractCoreLLMClient",
|
|
37
|
+
"AbstractCoreEmbeddingsClient",
|
|
38
|
+
"EmbeddingsResult",
|
|
35
39
|
"RuntimeConfig",
|
|
36
40
|
"ToolExecutor",
|
|
37
41
|
"MappingToolExecutor",
|
|
@@ -44,4 +48,6 @@ __all__ = [
|
|
|
44
48
|
"create_hybrid_runtime",
|
|
45
49
|
"create_local_file_runtime",
|
|
46
50
|
"create_remote_file_runtime",
|
|
51
|
+
"attach_global_event_bus_bridge",
|
|
52
|
+
"emit_step_record",
|
|
47
53
|
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""abstractruntime.integrations.abstractcore.constants
|
|
2
|
+
|
|
3
|
+
Single source of truth for AbstractRuntime orchestration defaults when it
|
|
4
|
+
executes AbstractCore-backed effects (LLM calls and tool calls).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Default *effect* timeouts (seconds).
|
|
8
|
+
#
|
|
9
|
+
# IMPORTANT: These are NOT a "workflow/run TTL". Workflows can be long-lived
|
|
10
|
+
# (hours/days) or even continuous. These limits only apply to a *single*
|
|
11
|
+
# runtime-managed operation (e.g. one LLM HTTP request, one tool call).
|
|
12
|
+
#
|
|
13
|
+
# Rationale:
|
|
14
|
+
# - Local inference can be slow for large contexts.
|
|
15
|
+
# - In an orchestrator, timeouts are policy and should be explicit + consistent.
|
|
16
|
+
DEFAULT_LLM_TIMEOUT_S: float = 7200.0
|
|
17
|
+
DEFAULT_TOOL_TIMEOUT_S: float = 7200.0
|
|
18
|
+
|
|
19
|
+
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Default toolsets for AbstractRuntime's AbstractCore integration.
|
|
2
|
+
|
|
3
|
+
This module provides a *host-side* convenience list of common, safe(ish) tools
|
|
4
|
+
that can be wired into a Runtime via MappingToolExecutor.
|
|
5
|
+
|
|
6
|
+
Design notes:
|
|
7
|
+
- We keep the runtime kernel dependency-light; this lives under
|
|
8
|
+
`integrations/abstractcore/` which is the explicit opt-in to AbstractCore.
|
|
9
|
+
- Tool callables are never persisted in RunState; only ToolSpecs (dicts) are.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
from typing import Any, Callable, Dict, List, Sequence
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ToolCallable = Callable[..., Any]
|
|
19
|
+
|
|
20
|
+
_COMMS_ENABLE_ENV_VARS = (
|
|
21
|
+
"ABSTRACT_ENABLE_COMMS_TOOLS",
|
|
22
|
+
"ABSTRACT_ENABLE_EMAIL_TOOLS",
|
|
23
|
+
"ABSTRACT_ENABLE_WHATSAPP_TOOLS",
|
|
24
|
+
"ABSTRACT_ENABLE_TELEGRAM_TOOLS",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _env_flag(name: str) -> bool:
|
|
29
|
+
raw = os.getenv(name)
|
|
30
|
+
if raw is None:
|
|
31
|
+
return False
|
|
32
|
+
return str(raw).strip().lower() in {"1", "true", "yes", "y", "on"}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def comms_tools_enabled() -> bool:
|
|
36
|
+
"""Return True when the host explicitly opts into comms tools via env."""
|
|
37
|
+
return any(_env_flag(k) for k in _COMMS_ENABLE_ENV_VARS)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def email_tools_enabled() -> bool:
|
|
41
|
+
return _env_flag("ABSTRACT_ENABLE_COMMS_TOOLS") or _env_flag("ABSTRACT_ENABLE_EMAIL_TOOLS")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def whatsapp_tools_enabled() -> bool:
|
|
45
|
+
return _env_flag("ABSTRACT_ENABLE_COMMS_TOOLS") or _env_flag("ABSTRACT_ENABLE_WHATSAPP_TOOLS")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def telegram_tools_enabled() -> bool:
|
|
49
|
+
return _env_flag("ABSTRACT_ENABLE_COMMS_TOOLS") or _env_flag("ABSTRACT_ENABLE_TELEGRAM_TOOLS")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _tool_name(func: ToolCallable) -> str:
|
|
53
|
+
tool_def = getattr(func, "_tool_definition", None)
|
|
54
|
+
if tool_def is not None:
|
|
55
|
+
name = getattr(tool_def, "name", None)
|
|
56
|
+
if isinstance(name, str) and name.strip():
|
|
57
|
+
return name.strip()
|
|
58
|
+
name = getattr(func, "__name__", "")
|
|
59
|
+
return str(name or "").strip()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _tool_spec(func: ToolCallable) -> Dict[str, Any]:
|
|
63
|
+
tool_def = getattr(func, "_tool_definition", None)
|
|
64
|
+
if tool_def is not None and hasattr(tool_def, "to_dict"):
|
|
65
|
+
return dict(tool_def.to_dict())
|
|
66
|
+
|
|
67
|
+
from abstractcore.tools.core import ToolDefinition
|
|
68
|
+
|
|
69
|
+
return dict(ToolDefinition.from_function(func).to_dict())
|
|
70
|
+
|
|
71
|
+
def _normalize_tool_spec(spec: Dict[str, Any]) -> Dict[str, Any]:
|
|
72
|
+
"""Normalize ToolSpec quirks for better UI/LLM ergonomics.
|
|
73
|
+
|
|
74
|
+
This is a presentation layer: it must not change tool execution semantics.
|
|
75
|
+
"""
|
|
76
|
+
name = str(spec.get("name") or "").strip()
|
|
77
|
+
if not name:
|
|
78
|
+
return spec
|
|
79
|
+
|
|
80
|
+
# AbstractCore's `skim_*` tools accept multiple paths and can parse strings for
|
|
81
|
+
# backward compatibility, but the preferred shape is an array-of-strings.
|
|
82
|
+
if name in {"skim_files", "skim_folders"}:
|
|
83
|
+
params = spec.get("parameters")
|
|
84
|
+
if isinstance(params, dict):
|
|
85
|
+
paths_schema = params.get("paths")
|
|
86
|
+
if isinstance(paths_schema, dict):
|
|
87
|
+
desc = paths_schema.get("description")
|
|
88
|
+
normalized = {
|
|
89
|
+
"type": "array",
|
|
90
|
+
"items": {"type": "string"},
|
|
91
|
+
}
|
|
92
|
+
if isinstance(desc, str) and desc.strip():
|
|
93
|
+
normalized["description"] = desc.strip()
|
|
94
|
+
params["paths"] = normalized
|
|
95
|
+
|
|
96
|
+
return spec
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_default_toolsets() -> Dict[str, Dict[str, Any]]:
|
|
100
|
+
"""Return default toolsets {id -> {label, tools:[callables]}}."""
|
|
101
|
+
from abstractcore.tools.common_tools import (
|
|
102
|
+
list_files,
|
|
103
|
+
skim_folders,
|
|
104
|
+
read_file,
|
|
105
|
+
skim_files,
|
|
106
|
+
search_files,
|
|
107
|
+
analyze_code,
|
|
108
|
+
write_file,
|
|
109
|
+
edit_file,
|
|
110
|
+
web_search,
|
|
111
|
+
fetch_url,
|
|
112
|
+
execute_command,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
toolsets: Dict[str, Dict[str, Any]] = {
|
|
116
|
+
"files": {
|
|
117
|
+
"id": "files",
|
|
118
|
+
"label": "Files",
|
|
119
|
+
"tools": [list_files, skim_folders, search_files, analyze_code, skim_files, read_file, write_file, edit_file],
|
|
120
|
+
},
|
|
121
|
+
"web": {
|
|
122
|
+
"id": "web",
|
|
123
|
+
"label": "Web",
|
|
124
|
+
"tools": [web_search, fetch_url],
|
|
125
|
+
},
|
|
126
|
+
"system": {
|
|
127
|
+
"id": "system",
|
|
128
|
+
"label": "System",
|
|
129
|
+
"tools": [execute_command],
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if comms_tools_enabled():
|
|
134
|
+
comms: list[ToolCallable] = []
|
|
135
|
+
if email_tools_enabled():
|
|
136
|
+
from abstractcore.tools.comms_tools import list_email_accounts, list_emails, read_email, send_email
|
|
137
|
+
|
|
138
|
+
comms.extend([list_email_accounts, send_email, list_emails, read_email])
|
|
139
|
+
if whatsapp_tools_enabled():
|
|
140
|
+
from abstractcore.tools.comms_tools import list_whatsapp_messages, read_whatsapp_message, send_whatsapp_message
|
|
141
|
+
|
|
142
|
+
comms.extend([send_whatsapp_message, list_whatsapp_messages, read_whatsapp_message])
|
|
143
|
+
if telegram_tools_enabled():
|
|
144
|
+
from abstractcore.tools.telegram_tools import send_telegram_artifact, send_telegram_message
|
|
145
|
+
|
|
146
|
+
comms.extend([send_telegram_message, send_telegram_artifact])
|
|
147
|
+
|
|
148
|
+
if comms:
|
|
149
|
+
toolsets["comms"] = {
|
|
150
|
+
"id": "comms",
|
|
151
|
+
"label": "Comms",
|
|
152
|
+
"tools": comms,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return toolsets
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def get_default_tools() -> List[ToolCallable]:
|
|
159
|
+
"""Return the flattened list of all default tool callables."""
|
|
160
|
+
toolsets = get_default_toolsets()
|
|
161
|
+
out: list[ToolCallable] = []
|
|
162
|
+
seen: set[str] = set()
|
|
163
|
+
for spec in toolsets.values():
|
|
164
|
+
for tool in spec.get("tools", []):
|
|
165
|
+
if not callable(tool):
|
|
166
|
+
continue
|
|
167
|
+
name = _tool_name(tool)
|
|
168
|
+
if not name or name in seen:
|
|
169
|
+
continue
|
|
170
|
+
seen.add(name)
|
|
171
|
+
out.append(tool)
|
|
172
|
+
return out
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def list_default_tool_specs() -> List[Dict[str, Any]]:
|
|
176
|
+
"""Return ToolSpecs for UI and LLM payloads (JSON-safe)."""
|
|
177
|
+
toolsets = get_default_toolsets()
|
|
178
|
+
toolset_by_name: Dict[str, str] = {}
|
|
179
|
+
for tid, spec in toolsets.items():
|
|
180
|
+
for tool in spec.get("tools", []):
|
|
181
|
+
if callable(tool):
|
|
182
|
+
name = _tool_name(tool)
|
|
183
|
+
if name:
|
|
184
|
+
toolset_by_name[name] = tid
|
|
185
|
+
|
|
186
|
+
out: list[Dict[str, Any]] = []
|
|
187
|
+
for tool in get_default_tools():
|
|
188
|
+
spec = _normalize_tool_spec(_tool_spec(tool))
|
|
189
|
+
name = str(spec.get("name") or "").strip()
|
|
190
|
+
if not name:
|
|
191
|
+
continue
|
|
192
|
+
spec["toolset"] = toolset_by_name.get(name) or "other"
|
|
193
|
+
out.append(spec)
|
|
194
|
+
|
|
195
|
+
# Runtime-owned tools (no host callable).
|
|
196
|
+
#
|
|
197
|
+
# Rationale: these tools require runtime context/state (e.g., session-scoped ArtifactStore access)
|
|
198
|
+
# and are executed inside the runtime effect handlers instead of via a host ToolExecutor.
|
|
199
|
+
out.append(
|
|
200
|
+
{
|
|
201
|
+
"name": "open_attachment",
|
|
202
|
+
"description": "Open a session attachment; returns text (excerpt or full if max_chars<=0) and attaches media for the next LLM call.",
|
|
203
|
+
"when_to_use": (
|
|
204
|
+
"Re-open a previously attached file; text returns an excerpt by default (or full when max_chars<=0), "
|
|
205
|
+
"media is attached as `media` on the next call. Do not call if it is already present in the current messages/media."
|
|
206
|
+
),
|
|
207
|
+
"parameters": {
|
|
208
|
+
"artifact_id": {"type": "string", "default": None},
|
|
209
|
+
"handle": {"type": "string", "default": None},
|
|
210
|
+
"expected_sha256": {"type": "string", "default": None},
|
|
211
|
+
"start_line": {"type": "integer", "default": 1},
|
|
212
|
+
"end_line": {"type": "integer", "default": None},
|
|
213
|
+
"max_chars": {"type": "integer", "default": 8000},
|
|
214
|
+
},
|
|
215
|
+
"required_args": [],
|
|
216
|
+
"examples": [
|
|
217
|
+
{
|
|
218
|
+
"description": "Open by artifact id (preferred)",
|
|
219
|
+
"arguments": {"artifact_id": "abc123", "start_line": 1, "end_line": 80},
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"description": "Open by handle",
|
|
223
|
+
"arguments": {"handle": "@docs/architecture.md", "max_chars": 2000},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"description": "Open full text (no cap)",
|
|
227
|
+
"arguments": {"artifact_id": "abc123", "max_chars": 0},
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
"toolset": "files",
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Stable ordering: toolset then name
|
|
235
|
+
out.sort(key=lambda s: (str(s.get("toolset") or ""), str(s.get("name") or "")))
|
|
236
|
+
return out
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def build_default_tool_map() -> Dict[str, ToolCallable]:
|
|
240
|
+
"""Return {tool_name -> callable} for MappingToolExecutor."""
|
|
241
|
+
tool_map: Dict[str, ToolCallable] = {}
|
|
242
|
+
for tool in get_default_tools():
|
|
243
|
+
name = _tool_name(tool)
|
|
244
|
+
if not name:
|
|
245
|
+
continue
|
|
246
|
+
tool_map[name] = tool
|
|
247
|
+
return tool_map
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def filter_tool_specs(tool_names: Sequence[str]) -> List[Dict[str, Any]]:
|
|
251
|
+
"""Return ToolSpecs for the requested tool names (order preserved)."""
|
|
252
|
+
available = {str(s.get("name")): s for s in list_default_tool_specs() if isinstance(s.get("name"), str)}
|
|
253
|
+
out: list[Dict[str, Any]] = []
|
|
254
|
+
for name in tool_names:
|
|
255
|
+
spec = available.get(name)
|
|
256
|
+
if spec is not None:
|
|
257
|
+
out.append(spec)
|
|
258
|
+
return out
|