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.
Files changed (77) hide show
  1. abstractruntime/__init__.py +83 -3
  2. abstractruntime/core/config.py +82 -2
  3. abstractruntime/core/event_keys.py +62 -0
  4. abstractruntime/core/models.py +17 -1
  5. abstractruntime/core/policy.py +74 -3
  6. abstractruntime/core/runtime.py +3334 -28
  7. abstractruntime/core/vars.py +103 -2
  8. abstractruntime/evidence/__init__.py +10 -0
  9. abstractruntime/evidence/recorder.py +325 -0
  10. abstractruntime/history_bundle.py +772 -0
  11. abstractruntime/integrations/abstractcore/__init__.py +6 -0
  12. abstractruntime/integrations/abstractcore/constants.py +19 -0
  13. abstractruntime/integrations/abstractcore/default_tools.py +258 -0
  14. abstractruntime/integrations/abstractcore/effect_handlers.py +2622 -32
  15. abstractruntime/integrations/abstractcore/embeddings_client.py +69 -0
  16. abstractruntime/integrations/abstractcore/factory.py +149 -16
  17. abstractruntime/integrations/abstractcore/llm_client.py +891 -55
  18. abstractruntime/integrations/abstractcore/mcp_worker.py +587 -0
  19. abstractruntime/integrations/abstractcore/observability.py +80 -0
  20. abstractruntime/integrations/abstractcore/session_attachments.py +946 -0
  21. abstractruntime/integrations/abstractcore/summarizer.py +154 -0
  22. abstractruntime/integrations/abstractcore/tool_executor.py +509 -31
  23. abstractruntime/integrations/abstractcore/workspace_scoped_tools.py +561 -0
  24. abstractruntime/integrations/abstractmemory/__init__.py +3 -0
  25. abstractruntime/integrations/abstractmemory/effect_handlers.py +946 -0
  26. abstractruntime/memory/__init__.py +21 -0
  27. abstractruntime/memory/active_context.py +751 -0
  28. abstractruntime/memory/active_memory.py +452 -0
  29. abstractruntime/memory/compaction.py +105 -0
  30. abstractruntime/memory/kg_packets.py +164 -0
  31. abstractruntime/memory/memact_composer.py +175 -0
  32. abstractruntime/memory/recall_levels.py +163 -0
  33. abstractruntime/memory/token_budget.py +86 -0
  34. abstractruntime/rendering/__init__.py +17 -0
  35. abstractruntime/rendering/agent_trace_report.py +256 -0
  36. abstractruntime/rendering/json_stringify.py +136 -0
  37. abstractruntime/scheduler/scheduler.py +93 -2
  38. abstractruntime/storage/__init__.py +7 -2
  39. abstractruntime/storage/artifacts.py +175 -32
  40. abstractruntime/storage/base.py +17 -1
  41. abstractruntime/storage/commands.py +339 -0
  42. abstractruntime/storage/in_memory.py +41 -1
  43. abstractruntime/storage/json_files.py +210 -14
  44. abstractruntime/storage/observable.py +136 -0
  45. abstractruntime/storage/offloading.py +433 -0
  46. abstractruntime/storage/sqlite.py +836 -0
  47. abstractruntime/visualflow_compiler/__init__.py +29 -0
  48. abstractruntime/visualflow_compiler/adapters/__init__.py +11 -0
  49. abstractruntime/visualflow_compiler/adapters/agent_adapter.py +126 -0
  50. abstractruntime/visualflow_compiler/adapters/context_adapter.py +109 -0
  51. abstractruntime/visualflow_compiler/adapters/control_adapter.py +615 -0
  52. abstractruntime/visualflow_compiler/adapters/effect_adapter.py +1051 -0
  53. abstractruntime/visualflow_compiler/adapters/event_adapter.py +307 -0
  54. abstractruntime/visualflow_compiler/adapters/function_adapter.py +97 -0
  55. abstractruntime/visualflow_compiler/adapters/memact_adapter.py +114 -0
  56. abstractruntime/visualflow_compiler/adapters/subflow_adapter.py +74 -0
  57. abstractruntime/visualflow_compiler/adapters/variable_adapter.py +316 -0
  58. abstractruntime/visualflow_compiler/compiler.py +3832 -0
  59. abstractruntime/visualflow_compiler/flow.py +247 -0
  60. abstractruntime/visualflow_compiler/visual/__init__.py +13 -0
  61. abstractruntime/visualflow_compiler/visual/agent_ids.py +29 -0
  62. abstractruntime/visualflow_compiler/visual/builtins.py +1376 -0
  63. abstractruntime/visualflow_compiler/visual/code_executor.py +214 -0
  64. abstractruntime/visualflow_compiler/visual/executor.py +2804 -0
  65. abstractruntime/visualflow_compiler/visual/models.py +211 -0
  66. abstractruntime/workflow_bundle/__init__.py +52 -0
  67. abstractruntime/workflow_bundle/models.py +236 -0
  68. abstractruntime/workflow_bundle/packer.py +317 -0
  69. abstractruntime/workflow_bundle/reader.py +87 -0
  70. abstractruntime/workflow_bundle/registry.py +587 -0
  71. abstractruntime-0.4.1.dist-info/METADATA +177 -0
  72. abstractruntime-0.4.1.dist-info/RECORD +86 -0
  73. abstractruntime-0.4.1.dist-info/entry_points.txt +2 -0
  74. abstractruntime-0.2.0.dist-info/METADATA +0 -163
  75. abstractruntime-0.2.0.dist-info/RECORD +0 -32
  76. {abstractruntime-0.2.0.dist-info → abstractruntime-0.4.1.dist-info}/WHEEL +0 -0
  77. {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