AbstractRuntime 0.4.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 (65) hide show
  1. abstractruntime/__init__.py +76 -1
  2. abstractruntime/core/config.py +68 -1
  3. abstractruntime/core/models.py +5 -0
  4. abstractruntime/core/policy.py +74 -3
  5. abstractruntime/core/runtime.py +1002 -126
  6. abstractruntime/core/vars.py +8 -2
  7. abstractruntime/evidence/recorder.py +1 -1
  8. abstractruntime/history_bundle.py +772 -0
  9. abstractruntime/integrations/abstractcore/__init__.py +3 -0
  10. abstractruntime/integrations/abstractcore/default_tools.py +127 -3
  11. abstractruntime/integrations/abstractcore/effect_handlers.py +2440 -99
  12. abstractruntime/integrations/abstractcore/embeddings_client.py +69 -0
  13. abstractruntime/integrations/abstractcore/factory.py +68 -20
  14. abstractruntime/integrations/abstractcore/llm_client.py +447 -15
  15. abstractruntime/integrations/abstractcore/mcp_worker.py +1 -0
  16. abstractruntime/integrations/abstractcore/session_attachments.py +946 -0
  17. abstractruntime/integrations/abstractcore/tool_executor.py +31 -10
  18. abstractruntime/integrations/abstractcore/workspace_scoped_tools.py +561 -0
  19. abstractruntime/integrations/abstractmemory/__init__.py +3 -0
  20. abstractruntime/integrations/abstractmemory/effect_handlers.py +946 -0
  21. abstractruntime/memory/active_context.py +6 -1
  22. abstractruntime/memory/kg_packets.py +164 -0
  23. abstractruntime/memory/memact_composer.py +175 -0
  24. abstractruntime/memory/recall_levels.py +163 -0
  25. abstractruntime/memory/token_budget.py +86 -0
  26. abstractruntime/storage/__init__.py +4 -1
  27. abstractruntime/storage/artifacts.py +158 -30
  28. abstractruntime/storage/base.py +17 -1
  29. abstractruntime/storage/commands.py +339 -0
  30. abstractruntime/storage/in_memory.py +41 -1
  31. abstractruntime/storage/json_files.py +195 -12
  32. abstractruntime/storage/observable.py +38 -1
  33. abstractruntime/storage/offloading.py +433 -0
  34. abstractruntime/storage/sqlite.py +836 -0
  35. abstractruntime/visualflow_compiler/__init__.py +29 -0
  36. abstractruntime/visualflow_compiler/adapters/__init__.py +11 -0
  37. abstractruntime/visualflow_compiler/adapters/agent_adapter.py +126 -0
  38. abstractruntime/visualflow_compiler/adapters/context_adapter.py +109 -0
  39. abstractruntime/visualflow_compiler/adapters/control_adapter.py +615 -0
  40. abstractruntime/visualflow_compiler/adapters/effect_adapter.py +1051 -0
  41. abstractruntime/visualflow_compiler/adapters/event_adapter.py +307 -0
  42. abstractruntime/visualflow_compiler/adapters/function_adapter.py +97 -0
  43. abstractruntime/visualflow_compiler/adapters/memact_adapter.py +114 -0
  44. abstractruntime/visualflow_compiler/adapters/subflow_adapter.py +74 -0
  45. abstractruntime/visualflow_compiler/adapters/variable_adapter.py +316 -0
  46. abstractruntime/visualflow_compiler/compiler.py +3832 -0
  47. abstractruntime/visualflow_compiler/flow.py +247 -0
  48. abstractruntime/visualflow_compiler/visual/__init__.py +13 -0
  49. abstractruntime/visualflow_compiler/visual/agent_ids.py +29 -0
  50. abstractruntime/visualflow_compiler/visual/builtins.py +1376 -0
  51. abstractruntime/visualflow_compiler/visual/code_executor.py +214 -0
  52. abstractruntime/visualflow_compiler/visual/executor.py +2804 -0
  53. abstractruntime/visualflow_compiler/visual/models.py +211 -0
  54. abstractruntime/workflow_bundle/__init__.py +52 -0
  55. abstractruntime/workflow_bundle/models.py +236 -0
  56. abstractruntime/workflow_bundle/packer.py +317 -0
  57. abstractruntime/workflow_bundle/reader.py +87 -0
  58. abstractruntime/workflow_bundle/registry.py +587 -0
  59. abstractruntime-0.4.1.dist-info/METADATA +177 -0
  60. abstractruntime-0.4.1.dist-info/RECORD +86 -0
  61. abstractruntime-0.4.0.dist-info/METADATA +0 -167
  62. abstractruntime-0.4.0.dist-info/RECORD +0 -49
  63. {abstractruntime-0.4.0.dist-info → abstractruntime-0.4.1.dist-info}/WHEEL +0 -0
  64. {abstractruntime-0.4.0.dist-info → abstractruntime-0.4.1.dist-info}/entry_points.txt +0 -0
  65. {abstractruntime-0.4.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 (
@@ -33,6 +34,8 @@ __all__ = [
33
34
  "AbstractCoreLLMClient",
34
35
  "LocalAbstractCoreLLMClient",
35
36
  "RemoteAbstractCoreLLMClient",
37
+ "AbstractCoreEmbeddingsClient",
38
+ "EmbeddingsResult",
36
39
  "RuntimeConfig",
37
40
  "ToolExecutor",
38
41
  "MappingToolExecutor",
@@ -11,11 +11,43 @@ Design notes:
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
+ import os
14
15
  from typing import Any, Callable, Dict, List, Sequence
15
16
 
16
17
 
17
18
  ToolCallable = Callable[..., Any]
18
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
+
19
51
 
20
52
  def _tool_name(func: ToolCallable) -> str:
21
53
  tool_def = getattr(func, "_tool_definition", None)
@@ -36,12 +68,41 @@ def _tool_spec(func: ToolCallable) -> Dict[str, Any]:
36
68
 
37
69
  return dict(ToolDefinition.from_function(func).to_dict())
38
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
+
39
98
 
40
99
  def get_default_toolsets() -> Dict[str, Dict[str, Any]]:
41
100
  """Return default toolsets {id -> {label, tools:[callables]}}."""
42
101
  from abstractcore.tools.common_tools import (
43
102
  list_files,
103
+ skim_folders,
44
104
  read_file,
105
+ skim_files,
45
106
  search_files,
46
107
  analyze_code,
47
108
  write_file,
@@ -51,11 +112,11 @@ def get_default_toolsets() -> Dict[str, Dict[str, Any]]:
51
112
  execute_command,
52
113
  )
53
114
 
54
- return {
115
+ toolsets: Dict[str, Dict[str, Any]] = {
55
116
  "files": {
56
117
  "id": "files",
57
118
  "label": "Files",
58
- "tools": [list_files, search_files, analyze_code, read_file, write_file, edit_file],
119
+ "tools": [list_files, skim_folders, search_files, analyze_code, skim_files, read_file, write_file, edit_file],
59
120
  },
60
121
  "web": {
61
122
  "id": "web",
@@ -69,6 +130,30 @@ def get_default_toolsets() -> Dict[str, Dict[str, Any]]:
69
130
  },
70
131
  }
71
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
+
72
157
 
73
158
  def get_default_tools() -> List[ToolCallable]:
74
159
  """Return the flattened list of all default tool callables."""
@@ -100,13 +185,52 @@ def list_default_tool_specs() -> List[Dict[str, Any]]:
100
185
 
101
186
  out: list[Dict[str, Any]] = []
102
187
  for tool in get_default_tools():
103
- spec = _tool_spec(tool)
188
+ spec = _normalize_tool_spec(_tool_spec(tool))
104
189
  name = str(spec.get("name") or "").strip()
105
190
  if not name:
106
191
  continue
107
192
  spec["toolset"] = toolset_by_name.get(name) or "other"
108
193
  out.append(spec)
109
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
+
110
234
  # Stable ordering: toolset then name
111
235
  out.sort(key=lambda s: (str(s.get("toolset") or ""), str(s.get("name") or "")))
112
236
  return out