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
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, List, Sequence
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class EmbeddingsResult:
9
+ provider: str
10
+ model: str
11
+ embeddings: List[List[float]]
12
+ dimension: int
13
+
14
+
15
+ class AbstractCoreEmbeddingsClient:
16
+ """Thin adapter around AbstractCore's EmbeddingManager.
17
+
18
+ Notes:
19
+ - AbstractCore is an optional dependency of AbstractRuntime; importing this module is an explicit opt-in.
20
+ - The embedding provider/model should be treated as a runtime-instance property (singleton) so all vectors
21
+ live in the same embedding space.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ *,
27
+ provider: str,
28
+ model: str,
29
+ manager_kwargs: dict[str, Any] | None = None,
30
+ ) -> None:
31
+ # Monorepo import hygiene:
32
+ # When running from the repo root, `abstractcore/` (project folder) can be imported as a
33
+ # namespace package and shadow the real python package at `abstractcore/abstractcore/`.
34
+ # In that situation, `abstractcore.embeddings.manager` may not resolve even though the
35
+ # implementation exists. Prefer the public import, but fall back to the concrete path.
36
+ try:
37
+ from abstractcore.embeddings.manager import EmbeddingManager # type: ignore
38
+ except Exception: # pragma: no cover
39
+ from abstractcore.abstractcore.embeddings.manager import EmbeddingManager # type: ignore
40
+
41
+ prov = str(provider or "").strip().lower()
42
+ mod = str(model or "").strip()
43
+ if not prov:
44
+ raise ValueError("provider is required for embeddings")
45
+ if not mod:
46
+ raise ValueError("model is required for embeddings")
47
+
48
+ self._provider = prov
49
+ self._model = mod
50
+ self._mgr = EmbeddingManager(provider=prov, model=mod, **(manager_kwargs or {}))
51
+
52
+ @property
53
+ def provider(self) -> str:
54
+ return self._provider
55
+
56
+ @property
57
+ def model(self) -> str:
58
+ return self._model
59
+
60
+ def embed_texts(self, texts: Sequence[str]) -> EmbeddingsResult:
61
+ items = [str(t or "") for t in texts]
62
+ embeddings = self._mgr.embed_batch(items)
63
+ dim = int(self._mgr.get_dimension() or 0)
64
+ return EmbeddingsResult(
65
+ provider=self._provider,
66
+ model=self._model,
67
+ embeddings=embeddings,
68
+ dimension=dim,
69
+ )
@@ -20,10 +20,14 @@ from ...core.runtime import Runtime
20
20
  from ...storage.in_memory import InMemoryLedgerStore, InMemoryRunStore
21
21
  from ...storage.json_files import JsonFileRunStore, JsonlLedgerStore
22
22
  from ...storage.base import LedgerStore, RunStore
23
+ from ...storage.artifacts import FileArtifactStore, InMemoryArtifactStore, ArtifactStore
24
+ from ...storage.observable import ObservableLedgerStore, ObservableLedgerStoreProtocol
23
25
 
24
26
  from .effect_handlers import build_effect_handlers
25
- from .llm_client import LocalAbstractCoreLLMClient, RemoteAbstractCoreLLMClient
27
+ from .llm_client import MultiLocalAbstractCoreLLMClient, RemoteAbstractCoreLLMClient
26
28
  from .tool_executor import AbstractCoreToolExecutor, PassthroughToolExecutor, ToolExecutor
29
+ from .summarizer import AbstractCoreChatSummarizer
30
+ from .constants import DEFAULT_LLM_TIMEOUT_S, DEFAULT_TOOL_TIMEOUT_S
27
31
 
28
32
 
29
33
  def _default_in_memory_stores() -> tuple[RunStore, LedgerStore]:
@@ -35,6 +39,18 @@ def _default_file_stores(*, base_dir: str | Path) -> tuple[RunStore, LedgerStore
35
39
  base.mkdir(parents=True, exist_ok=True)
36
40
  return JsonFileRunStore(base), JsonlLedgerStore(base)
37
41
 
42
+ def _ensure_observable_ledger(ledger_store: LedgerStore) -> LedgerStore:
43
+ """Wrap a LedgerStore so Runtime.subscribe_ledger() is available (in-process).
44
+
45
+ Why:
46
+ - Real-time UI/UX often needs "step started" signals *before* a blocking effect
47
+ (LLM/tool HTTP) returns.
48
+ - The runtime kernel stays transport-agnostic; this is an optional decorator.
49
+ """
50
+ if isinstance(ledger_store, ObservableLedgerStoreProtocol):
51
+ return ledger_store
52
+ return ObservableLedgerStore(ledger_store)
53
+
38
54
 
39
55
  def create_local_runtime(
40
56
  *,
@@ -44,9 +60,12 @@ def create_local_runtime(
44
60
  run_store: Optional[RunStore] = None,
45
61
  ledger_store: Optional[LedgerStore] = None,
46
62
  tool_executor: Optional[ToolExecutor] = None,
63
+ tool_timeout_s: Optional[float] = None,
47
64
  context: Optional[Any] = None,
48
65
  effect_policy: Optional[Any] = None,
49
66
  config: Optional[RuntimeConfig] = None,
67
+ artifact_store: Optional[ArtifactStore] = None,
68
+ extra_effect_handlers: Optional[Dict[Any, Any]] = None,
50
69
  ) -> Runtime:
51
70
  """Create a runtime with local LLM execution via AbstractCore.
52
71
 
@@ -69,27 +88,82 @@ def create_local_runtime(
69
88
  """
70
89
  if run_store is None or ledger_store is None:
71
90
  run_store, ledger_store = _default_in_memory_stores()
91
+ ledger_store = _ensure_observable_ledger(ledger_store)
92
+
93
+ if artifact_store is None:
94
+ artifact_store = InMemoryArtifactStore()
95
+
96
+ # Runtime authority: choose default timeouts for orchestrated workflows.
97
+ #
98
+ # Note: local providers may ignore timeouts (e.g. MLX) and should warn explicitly when doing so.
99
+ # We still pass a timeout through the runtime for future-proofing and consistent orchestration
100
+ # policy across providers.
101
+ default_llm_timeout_s: float = DEFAULT_LLM_TIMEOUT_S
102
+ default_tool_timeout_s: float = DEFAULT_TOOL_TIMEOUT_S
103
+ try:
104
+ from abstractcore.config.manager import get_config_manager # type: ignore
105
+
106
+ cfg_mgr = get_config_manager()
107
+ default_llm_timeout_s = float(cfg_mgr.get_default_timeout())
108
+ default_tool_timeout_s = float(cfg_mgr.get_tool_timeout())
109
+ except Exception:
110
+ pass
111
+
112
+ resolved_tool_timeout_s = float(tool_timeout_s) if tool_timeout_s is not None else float(default_tool_timeout_s)
113
+
114
+ effective_llm_kwargs: Dict[str, Any] = dict(llm_kwargs or {})
115
+ effective_llm_kwargs.setdefault("timeout", float(default_llm_timeout_s))
72
116
 
73
- llm_client = LocalAbstractCoreLLMClient(provider=provider, model=model, llm_kwargs=llm_kwargs)
74
- tools = tool_executor or AbstractCoreToolExecutor()
75
- handlers = build_effect_handlers(llm=llm_client, tools=tools)
117
+ llm_client = MultiLocalAbstractCoreLLMClient(provider=provider, model=model, llm_kwargs=effective_llm_kwargs)
118
+ tools = tool_executor or AbstractCoreToolExecutor(timeout_s=resolved_tool_timeout_s)
119
+ # Orchestrator policy: enforce tool execution timeout at the runtime layer.
120
+ try:
121
+ setter = getattr(tools, "set_timeout_s", None)
122
+ if callable(setter):
123
+ setter(resolved_tool_timeout_s)
124
+ except Exception:
125
+ pass
126
+ handlers = build_effect_handlers(llm=llm_client, tools=tools, artifact_store=artifact_store, run_store=run_store)
127
+ if extra_effect_handlers:
128
+ handlers.update(dict(extra_effect_handlers))
76
129
 
77
130
  # Query model capabilities and merge into config
78
131
  capabilities = llm_client.get_model_capabilities()
79
132
  if config is None:
80
- config = RuntimeConfig(model_capabilities=capabilities)
133
+ config = RuntimeConfig(
134
+ provider=str(provider).strip() if isinstance(provider, str) and str(provider).strip() else None,
135
+ model=str(model).strip() if isinstance(model, str) and str(model).strip() else None,
136
+ model_capabilities=capabilities,
137
+ )
81
138
  else:
82
139
  # Merge capabilities into provided config
83
140
  config = config.with_capabilities(capabilities)
84
141
 
85
- return Runtime(
142
+ # Create chat summarizer with token limits from config
143
+ # This enables adaptive chunking during MEMORY_COMPACT
144
+ summarizer = AbstractCoreChatSummarizer(
145
+ llm=llm_client._llm, # Use the underlying AbstractCore LLM instance
146
+ max_tokens=config.max_tokens if config.max_tokens is not None else -1,
147
+ max_output_tokens=config.max_output_tokens if config.max_output_tokens is not None else -1,
148
+ )
149
+
150
+ rt = Runtime(
86
151
  run_store=run_store,
87
152
  ledger_store=ledger_store,
88
153
  effect_handlers=handlers,
89
154
  context=context,
90
155
  effect_policy=effect_policy,
91
156
  config=config,
157
+ artifact_store=artifact_store,
158
+ chat_summarizer=summarizer,
92
159
  )
160
+ # Best-effort: expose the underlying LLM client for host-side control planes (e.g. gateway cache ops).
161
+ # This stays an internal attribute to avoid hard API commitments on Runtime itself.
162
+ try: # pragma: no cover
163
+ setattr(rt, "_abstractcore_llm_client", llm_client)
164
+ except Exception:
165
+ pass
166
+ return rt
93
167
 
94
168
 
95
169
  def create_remote_runtime(
@@ -97,25 +171,45 @@ def create_remote_runtime(
97
171
  server_base_url: str,
98
172
  model: str,
99
173
  headers: Optional[Dict[str, str]] = None,
100
- timeout_s: float = 60.0,
174
+ timeout_s: Optional[float] = None,
101
175
  run_store: Optional[RunStore] = None,
102
176
  ledger_store: Optional[LedgerStore] = None,
103
177
  tool_executor: Optional[ToolExecutor] = None,
104
178
  context: Optional[Any] = None,
179
+ artifact_store: Optional[ArtifactStore] = None,
105
180
  ) -> Runtime:
106
181
  if run_store is None or ledger_store is None:
107
182
  run_store, ledger_store = _default_in_memory_stores()
183
+ ledger_store = _ensure_observable_ledger(ledger_store)
184
+
185
+ if artifact_store is None:
186
+ artifact_store = InMemoryArtifactStore()
187
+
188
+ resolved_timeout_s = float(timeout_s) if timeout_s is not None else float(DEFAULT_LLM_TIMEOUT_S)
189
+ if timeout_s is None:
190
+ try:
191
+ from abstractcore.config.manager import get_config_manager # type: ignore
192
+
193
+ resolved_timeout_s = float(get_config_manager().get_default_timeout())
194
+ except Exception:
195
+ pass
108
196
 
109
197
  llm_client = RemoteAbstractCoreLLMClient(
110
198
  server_base_url=server_base_url,
111
199
  model=model,
112
200
  headers=headers,
113
- timeout_s=timeout_s,
201
+ timeout_s=resolved_timeout_s,
114
202
  )
115
203
  tools = tool_executor or PassthroughToolExecutor()
116
- handlers = build_effect_handlers(llm=llm_client, tools=tools)
204
+ handlers = build_effect_handlers(llm=llm_client, tools=tools, artifact_store=artifact_store, run_store=run_store)
117
205
 
118
- return Runtime(run_store=run_store, ledger_store=ledger_store, effect_handlers=handlers, context=context)
206
+ return Runtime(
207
+ run_store=run_store,
208
+ ledger_store=ledger_store,
209
+ effect_handlers=handlers,
210
+ context=context,
211
+ artifact_store=artifact_store,
212
+ )
119
213
 
120
214
 
121
215
  def create_hybrid_runtime(
@@ -123,26 +217,59 @@ def create_hybrid_runtime(
123
217
  server_base_url: str,
124
218
  model: str,
125
219
  headers: Optional[Dict[str, str]] = None,
126
- timeout_s: float = 60.0,
220
+ timeout_s: Optional[float] = None,
221
+ tool_timeout_s: Optional[float] = None,
127
222
  run_store: Optional[RunStore] = None,
128
223
  ledger_store: Optional[LedgerStore] = None,
129
224
  context: Optional[Any] = None,
225
+ artifact_store: Optional[ArtifactStore] = None,
130
226
  ) -> Runtime:
131
227
  """Remote LLM via AbstractCore server, local tool execution."""
132
228
 
133
229
  if run_store is None or ledger_store is None:
134
230
  run_store, ledger_store = _default_in_memory_stores()
231
+ ledger_store = _ensure_observable_ledger(ledger_store)
232
+
233
+ if artifact_store is None:
234
+ artifact_store = InMemoryArtifactStore()
235
+
236
+ default_llm_timeout_s = float(DEFAULT_LLM_TIMEOUT_S)
237
+ default_tool_timeout_s = float(DEFAULT_TOOL_TIMEOUT_S)
238
+ if timeout_s is None or tool_timeout_s is None:
239
+ try:
240
+ from abstractcore.config.manager import get_config_manager # type: ignore
241
+
242
+ cfg_mgr = get_config_manager()
243
+ default_llm_timeout_s = float(cfg_mgr.get_default_timeout())
244
+ default_tool_timeout_s = float(cfg_mgr.get_tool_timeout())
245
+ except Exception:
246
+ pass
247
+
248
+ resolved_timeout_s = float(timeout_s) if timeout_s is not None else float(default_llm_timeout_s)
249
+ resolved_tool_timeout_s = float(tool_timeout_s) if tool_timeout_s is not None else float(default_tool_timeout_s)
135
250
 
136
251
  llm_client = RemoteAbstractCoreLLMClient(
137
252
  server_base_url=server_base_url,
138
253
  model=model,
139
254
  headers=headers,
140
- timeout_s=timeout_s,
255
+ timeout_s=resolved_timeout_s,
141
256
  )
142
- tools = AbstractCoreToolExecutor()
143
- handlers = build_effect_handlers(llm=llm_client, tools=tools)
257
+ tools = AbstractCoreToolExecutor(timeout_s=resolved_tool_timeout_s)
258
+ try:
259
+ setter = getattr(tools, "set_timeout_s", None)
260
+ if callable(setter):
261
+ setter(resolved_tool_timeout_s)
262
+ except Exception:
263
+ pass
264
+ handlers = build_effect_handlers(llm=llm_client, tools=tools, artifact_store=artifact_store, run_store=run_store)
144
265
 
145
- return Runtime(run_store=run_store, ledger_store=ledger_store, effect_handlers=handlers, context=context)
266
+ return Runtime(
267
+ run_store=run_store,
268
+ ledger_store=ledger_store,
269
+ effect_handlers=handlers,
270
+ context=context,
271
+ artifact_store=artifact_store,
272
+ )
146
273
 
147
274
 
148
275
  def create_local_file_runtime(
@@ -153,8 +280,10 @@ def create_local_file_runtime(
153
280
  llm_kwargs: Optional[Dict[str, Any]] = None,
154
281
  context: Optional[Any] = None,
155
282
  config: Optional[RuntimeConfig] = None,
283
+ tool_timeout_s: Optional[float] = None,
156
284
  ) -> Runtime:
157
285
  run_store, ledger_store = _default_file_stores(base_dir=base_dir)
286
+ artifact_store = FileArtifactStore(base_dir)
158
287
  return create_local_runtime(
159
288
  provider=provider,
160
289
  model=model,
@@ -163,6 +292,8 @@ def create_local_file_runtime(
163
292
  ledger_store=ledger_store,
164
293
  context=context,
165
294
  config=config,
295
+ artifact_store=artifact_store,
296
+ tool_timeout_s=tool_timeout_s,
166
297
  )
167
298
 
168
299
 
@@ -172,10 +303,11 @@ def create_remote_file_runtime(
172
303
  server_base_url: str,
173
304
  model: str,
174
305
  headers: Optional[Dict[str, str]] = None,
175
- timeout_s: float = 60.0,
306
+ timeout_s: Optional[float] = None,
176
307
  context: Optional[Any] = None,
177
308
  ) -> Runtime:
178
309
  run_store, ledger_store = _default_file_stores(base_dir=base_dir)
310
+ artifact_store = FileArtifactStore(base_dir)
179
311
  return create_remote_runtime(
180
312
  server_base_url=server_base_url,
181
313
  model=model,
@@ -184,4 +316,5 @@ def create_remote_file_runtime(
184
316
  run_store=run_store,
185
317
  ledger_store=ledger_store,
186
318
  context=context,
319
+ artifact_store=artifact_store,
187
320
  )