jarviscore-framework 0.1.0__py3-none-any.whl → 0.1.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.
- jarviscore/__init__.py +37 -5
- jarviscore/adapter/__init__.py +34 -0
- jarviscore/adapter/decorator.py +332 -0
- jarviscore/cli/check.py +18 -13
- jarviscore/cli/scaffold.py +178 -0
- jarviscore/context/__init__.py +40 -0
- jarviscore/context/dependency.py +160 -0
- jarviscore/context/jarvis_context.py +207 -0
- jarviscore/context/memory.py +155 -0
- jarviscore/data/.env.example +146 -0
- jarviscore/data/__init__.py +7 -0
- jarviscore/data/examples/calculator_agent_example.py +77 -0
- jarviscore/data/examples/multi_agent_workflow.py +132 -0
- jarviscore/data/examples/research_agent_example.py +76 -0
- jarviscore/docs/CONFIGURATION.md +6 -2
- jarviscore/docs/GETTING_STARTED.md +7 -4
- jarviscore/docs/TROUBLESHOOTING.md +11 -7
- jarviscore/docs/USER_GUIDE.md +6 -2
- jarviscore/execution/llm.py +23 -16
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.1.1.dist-info}/METADATA +10 -9
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.1.1.dist-info}/RECORD +26 -12
- tests/test_context.py +467 -0
- tests/test_decorator.py +622 -0
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.1.1.dist-info}/WHEEL +0 -0
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.1.1.dist-info}/licenses/LICENSE +0 -0
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.1.1.dist-info}/top_level.txt +0 -0
tests/test_context.py
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for JarvisCore context module.
|
|
3
|
+
|
|
4
|
+
Tests MemoryAccessor, DependencyAccessor, and JarvisContext classes
|
|
5
|
+
which provide the orchestration primitives for Custom Profile agents.
|
|
6
|
+
"""
|
|
7
|
+
import pytest
|
|
8
|
+
from jarviscore.context import (
|
|
9
|
+
JarvisContext,
|
|
10
|
+
MemoryAccessor,
|
|
11
|
+
DependencyAccessor,
|
|
12
|
+
create_context
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestMemoryAccessor:
|
|
17
|
+
"""Tests for MemoryAccessor class."""
|
|
18
|
+
|
|
19
|
+
def test_init_with_empty_memory(self):
|
|
20
|
+
"""Test initialization with empty memory dict."""
|
|
21
|
+
memory = MemoryAccessor({}, "step1")
|
|
22
|
+
assert len(memory) == 0
|
|
23
|
+
assert memory.keys() == []
|
|
24
|
+
|
|
25
|
+
def test_init_with_existing_memory(self):
|
|
26
|
+
"""Test initialization with pre-populated memory."""
|
|
27
|
+
data = {"step0": {"status": "success", "output": [1, 2, 3]}}
|
|
28
|
+
memory = MemoryAccessor(data, "step1")
|
|
29
|
+
assert len(memory) == 1
|
|
30
|
+
assert "step0" in memory.keys()
|
|
31
|
+
|
|
32
|
+
def test_get_extracts_output(self):
|
|
33
|
+
"""Test that get() extracts 'output' from result dicts."""
|
|
34
|
+
data = {
|
|
35
|
+
"step0": {"status": "success", "output": {"result": "data"}}
|
|
36
|
+
}
|
|
37
|
+
memory = MemoryAccessor(data, "step1")
|
|
38
|
+
|
|
39
|
+
result = memory.get("step0")
|
|
40
|
+
assert result == {"result": "data"}
|
|
41
|
+
|
|
42
|
+
def test_get_returns_raw_value_if_no_output_key(self):
|
|
43
|
+
"""Test get() returns raw value if no 'output' key."""
|
|
44
|
+
data = {"step0": "raw_string_value"}
|
|
45
|
+
memory = MemoryAccessor(data, "step1")
|
|
46
|
+
|
|
47
|
+
result = memory.get("step0")
|
|
48
|
+
assert result == "raw_string_value"
|
|
49
|
+
|
|
50
|
+
def test_get_returns_default_for_missing_key(self):
|
|
51
|
+
"""Test get() returns default for missing key."""
|
|
52
|
+
memory = MemoryAccessor({}, "step1")
|
|
53
|
+
|
|
54
|
+
assert memory.get("missing") is None
|
|
55
|
+
assert memory.get("missing", default="fallback") == "fallback"
|
|
56
|
+
assert memory.get("missing", default=[]) == []
|
|
57
|
+
|
|
58
|
+
def test_get_raw_returns_full_dict(self):
|
|
59
|
+
"""Test get_raw() returns full result dict."""
|
|
60
|
+
data = {
|
|
61
|
+
"step0": {"status": "success", "output": "data", "agent": "agent1"}
|
|
62
|
+
}
|
|
63
|
+
memory = MemoryAccessor(data, "step1")
|
|
64
|
+
|
|
65
|
+
result = memory.get_raw("step0")
|
|
66
|
+
assert result == {"status": "success", "output": "data", "agent": "agent1"}
|
|
67
|
+
|
|
68
|
+
def test_put_stores_value(self):
|
|
69
|
+
"""Test put() stores value in memory."""
|
|
70
|
+
data = {}
|
|
71
|
+
memory = MemoryAccessor(data, "step1")
|
|
72
|
+
|
|
73
|
+
memory.put("intermediate", {"processed": True})
|
|
74
|
+
|
|
75
|
+
assert "intermediate" in data
|
|
76
|
+
assert data["intermediate"] == {"processed": True}
|
|
77
|
+
|
|
78
|
+
def test_has_returns_true_for_existing_key(self):
|
|
79
|
+
"""Test has() returns True for existing key."""
|
|
80
|
+
data = {"step0": "value"}
|
|
81
|
+
memory = MemoryAccessor(data, "step1")
|
|
82
|
+
|
|
83
|
+
assert memory.has("step0") is True
|
|
84
|
+
assert memory.has("missing") is False
|
|
85
|
+
|
|
86
|
+
def test_all_extracts_outputs(self):
|
|
87
|
+
"""Test all() extracts outputs from all results."""
|
|
88
|
+
data = {
|
|
89
|
+
"step0": {"status": "success", "output": [1, 2, 3]},
|
|
90
|
+
"step1": {"status": "success", "output": {"key": "value"}},
|
|
91
|
+
"raw": "raw_value"
|
|
92
|
+
}
|
|
93
|
+
memory = MemoryAccessor(data, "step2")
|
|
94
|
+
|
|
95
|
+
result = memory.all()
|
|
96
|
+
|
|
97
|
+
assert result["step0"] == [1, 2, 3]
|
|
98
|
+
assert result["step1"] == {"key": "value"}
|
|
99
|
+
assert result["raw"] == "raw_value"
|
|
100
|
+
|
|
101
|
+
def test_keys_returns_all_keys(self):
|
|
102
|
+
"""Test keys() returns all memory keys."""
|
|
103
|
+
data = {"step0": "a", "step1": "b", "step2": "c"}
|
|
104
|
+
memory = MemoryAccessor(data, "step3")
|
|
105
|
+
|
|
106
|
+
keys = memory.keys()
|
|
107
|
+
|
|
108
|
+
assert set(keys) == {"step0", "step1", "step2"}
|
|
109
|
+
|
|
110
|
+
def test_contains_operator(self):
|
|
111
|
+
"""Test 'in' operator support."""
|
|
112
|
+
data = {"step0": "value"}
|
|
113
|
+
memory = MemoryAccessor(data, "step1")
|
|
114
|
+
|
|
115
|
+
assert "step0" in memory
|
|
116
|
+
assert "missing" not in memory
|
|
117
|
+
|
|
118
|
+
def test_getitem_operator(self):
|
|
119
|
+
"""Test dict-style access with []."""
|
|
120
|
+
data = {"step0": {"status": "success", "output": "data"}}
|
|
121
|
+
memory = MemoryAccessor(data, "step1")
|
|
122
|
+
|
|
123
|
+
assert memory["step0"] == "data"
|
|
124
|
+
|
|
125
|
+
def test_setitem_operator(self):
|
|
126
|
+
"""Test dict-style assignment with []."""
|
|
127
|
+
data = {}
|
|
128
|
+
memory = MemoryAccessor(data, "step1")
|
|
129
|
+
|
|
130
|
+
memory["key"] = "value"
|
|
131
|
+
|
|
132
|
+
assert data["key"] == "value"
|
|
133
|
+
|
|
134
|
+
def test_len(self):
|
|
135
|
+
"""Test len() returns number of items."""
|
|
136
|
+
data = {"a": 1, "b": 2, "c": 3}
|
|
137
|
+
memory = MemoryAccessor(data, "step1")
|
|
138
|
+
|
|
139
|
+
assert len(memory) == 3
|
|
140
|
+
|
|
141
|
+
def test_repr(self):
|
|
142
|
+
"""Test string representation."""
|
|
143
|
+
data = {"step0": "a", "step1": "b"}
|
|
144
|
+
memory = MemoryAccessor(data, "step2")
|
|
145
|
+
|
|
146
|
+
repr_str = repr(memory)
|
|
147
|
+
|
|
148
|
+
assert "MemoryAccessor" in repr_str
|
|
149
|
+
assert "step0" in repr_str or "keys=" in repr_str
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class TestDependencyAccessor:
|
|
153
|
+
"""Tests for DependencyAccessor class."""
|
|
154
|
+
|
|
155
|
+
def test_init_without_manager(self):
|
|
156
|
+
"""Test initialization without dependency manager."""
|
|
157
|
+
memory = {"step0": "value"}
|
|
158
|
+
deps = DependencyAccessor(None, memory)
|
|
159
|
+
|
|
160
|
+
assert deps._manager is None
|
|
161
|
+
assert deps._memory == memory
|
|
162
|
+
|
|
163
|
+
def test_is_ready_returns_true_for_existing_step(self):
|
|
164
|
+
"""Test is_ready() returns True for step in memory."""
|
|
165
|
+
memory = {"step0": {"output": "data"}}
|
|
166
|
+
deps = DependencyAccessor(None, memory)
|
|
167
|
+
|
|
168
|
+
assert deps.is_ready("step0") is True
|
|
169
|
+
assert deps.is_ready("missing") is False
|
|
170
|
+
|
|
171
|
+
def test_all_ready_returns_true_when_all_present(self):
|
|
172
|
+
"""Test all_ready() returns True when all steps present."""
|
|
173
|
+
memory = {"step0": "a", "step1": "b", "step2": "c"}
|
|
174
|
+
deps = DependencyAccessor(None, memory)
|
|
175
|
+
|
|
176
|
+
assert deps.all_ready(["step0", "step1"]) is True
|
|
177
|
+
assert deps.all_ready(["step0", "step1", "step2"]) is True
|
|
178
|
+
assert deps.all_ready(["step0", "missing"]) is False
|
|
179
|
+
|
|
180
|
+
def test_any_ready_returns_true_when_any_present(self):
|
|
181
|
+
"""Test any_ready() returns True when any step present."""
|
|
182
|
+
memory = {"step0": "a"}
|
|
183
|
+
deps = DependencyAccessor(None, memory)
|
|
184
|
+
|
|
185
|
+
assert deps.any_ready(["step0", "missing"]) is True
|
|
186
|
+
assert deps.any_ready(["missing1", "missing2"]) is False
|
|
187
|
+
|
|
188
|
+
def test_check_without_manager(self):
|
|
189
|
+
"""Test check() works without manager (fallback mode)."""
|
|
190
|
+
memory = {"step0": "a", "step1": "b"}
|
|
191
|
+
deps = DependencyAccessor(None, memory)
|
|
192
|
+
|
|
193
|
+
ready, missing = deps.check(["step0", "step1"])
|
|
194
|
+
assert ready is True
|
|
195
|
+
assert missing == []
|
|
196
|
+
|
|
197
|
+
ready, missing = deps.check(["step0", "step2"])
|
|
198
|
+
assert ready is False
|
|
199
|
+
assert missing == ["step2"]
|
|
200
|
+
|
|
201
|
+
def test_simple_wait_without_manager(self):
|
|
202
|
+
"""Test _simple_wait() extracts outputs."""
|
|
203
|
+
memory = {
|
|
204
|
+
"step0": {"status": "success", "output": [1, 2, 3]},
|
|
205
|
+
"step1": "raw_value"
|
|
206
|
+
}
|
|
207
|
+
deps = DependencyAccessor(None, memory)
|
|
208
|
+
|
|
209
|
+
result = deps._simple_wait(["step0", "step1"])
|
|
210
|
+
|
|
211
|
+
assert result["step0"] == [1, 2, 3]
|
|
212
|
+
assert result["step1"] == "raw_value"
|
|
213
|
+
|
|
214
|
+
def test_repr(self):
|
|
215
|
+
"""Test string representation."""
|
|
216
|
+
memory = {"step0": "a", "step1": "b"}
|
|
217
|
+
deps = DependencyAccessor(None, memory)
|
|
218
|
+
|
|
219
|
+
repr_str = repr(deps)
|
|
220
|
+
|
|
221
|
+
assert "DependencyAccessor" in repr_str
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class TestJarvisContext:
|
|
225
|
+
"""Tests for JarvisContext class."""
|
|
226
|
+
|
|
227
|
+
@pytest.fixture
|
|
228
|
+
def sample_memory(self):
|
|
229
|
+
"""Sample memory dict for tests."""
|
|
230
|
+
return {
|
|
231
|
+
"step0": {"status": "success", "output": {"data": [1, 2, 3]}},
|
|
232
|
+
"step1": {"status": "success", "output": "processed"}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@pytest.fixture
|
|
236
|
+
def sample_context(self, sample_memory):
|
|
237
|
+
"""Create a sample JarvisContext."""
|
|
238
|
+
memory_accessor = MemoryAccessor(sample_memory, "step2")
|
|
239
|
+
dep_accessor = DependencyAccessor(None, sample_memory)
|
|
240
|
+
|
|
241
|
+
return JarvisContext(
|
|
242
|
+
workflow_id="test-workflow",
|
|
243
|
+
step_id="step2",
|
|
244
|
+
task="Process data",
|
|
245
|
+
params={"threshold": 0.5, "mode": "fast"},
|
|
246
|
+
memory=memory_accessor,
|
|
247
|
+
deps=dep_accessor
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def test_context_attributes(self, sample_context):
|
|
251
|
+
"""Test context has correct attributes."""
|
|
252
|
+
assert sample_context.workflow_id == "test-workflow"
|
|
253
|
+
assert sample_context.step_id == "step2"
|
|
254
|
+
assert sample_context.task == "Process data"
|
|
255
|
+
assert sample_context.params == {"threshold": 0.5, "mode": "fast"}
|
|
256
|
+
|
|
257
|
+
def test_previous_gets_step_output(self, sample_context):
|
|
258
|
+
"""Test previous() retrieves step output."""
|
|
259
|
+
result = sample_context.previous("step0")
|
|
260
|
+
assert result == {"data": [1, 2, 3]}
|
|
261
|
+
|
|
262
|
+
result = sample_context.previous("step1")
|
|
263
|
+
assert result == "processed"
|
|
264
|
+
|
|
265
|
+
def test_previous_returns_default_for_missing(self, sample_context):
|
|
266
|
+
"""Test previous() returns default for missing step."""
|
|
267
|
+
result = sample_context.previous("missing")
|
|
268
|
+
assert result is None
|
|
269
|
+
|
|
270
|
+
result = sample_context.previous("missing", default={})
|
|
271
|
+
assert result == {}
|
|
272
|
+
|
|
273
|
+
def test_all_previous_gets_all_outputs(self, sample_context):
|
|
274
|
+
"""Test all_previous() retrieves all outputs."""
|
|
275
|
+
result = sample_context.all_previous()
|
|
276
|
+
|
|
277
|
+
assert "step0" in result
|
|
278
|
+
assert "step1" in result
|
|
279
|
+
assert result["step0"] == {"data": [1, 2, 3]}
|
|
280
|
+
assert result["step1"] == "processed"
|
|
281
|
+
|
|
282
|
+
def test_previous_results_property(self, sample_context):
|
|
283
|
+
"""Test previous_results property is alias for all_previous()."""
|
|
284
|
+
result = sample_context.previous_results
|
|
285
|
+
assert result == sample_context.all_previous()
|
|
286
|
+
|
|
287
|
+
def test_has_previous(self, sample_context):
|
|
288
|
+
"""Test has_previous() checks step existence."""
|
|
289
|
+
assert sample_context.has_previous("step0") is True
|
|
290
|
+
assert sample_context.has_previous("step1") is True
|
|
291
|
+
assert sample_context.has_previous("missing") is False
|
|
292
|
+
|
|
293
|
+
def test_get_param(self, sample_context):
|
|
294
|
+
"""Test get_param() retrieves task parameters."""
|
|
295
|
+
assert sample_context.get_param("threshold") == 0.5
|
|
296
|
+
assert sample_context.get_param("mode") == "fast"
|
|
297
|
+
assert sample_context.get_param("missing") is None
|
|
298
|
+
assert sample_context.get_param("missing", default=1.0) == 1.0
|
|
299
|
+
|
|
300
|
+
def test_memory_accessor_available(self, sample_context):
|
|
301
|
+
"""Test memory accessor is available on context."""
|
|
302
|
+
assert sample_context.memory is not None
|
|
303
|
+
assert isinstance(sample_context.memory, MemoryAccessor)
|
|
304
|
+
assert sample_context.memory.get("step0") == {"data": [1, 2, 3]}
|
|
305
|
+
|
|
306
|
+
def test_deps_accessor_available(self, sample_context):
|
|
307
|
+
"""Test dependency accessor is available on context."""
|
|
308
|
+
assert sample_context.deps is not None
|
|
309
|
+
assert isinstance(sample_context.deps, DependencyAccessor)
|
|
310
|
+
assert sample_context.deps.is_ready("step0") is True
|
|
311
|
+
|
|
312
|
+
def test_context_with_none_memory(self):
|
|
313
|
+
"""Test context handles None memory gracefully."""
|
|
314
|
+
ctx = JarvisContext(
|
|
315
|
+
workflow_id="test",
|
|
316
|
+
step_id="step0",
|
|
317
|
+
task="test",
|
|
318
|
+
params={},
|
|
319
|
+
memory=None,
|
|
320
|
+
deps=None
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
assert ctx.previous("any") is None
|
|
324
|
+
assert ctx.all_previous() == {}
|
|
325
|
+
assert ctx.has_previous("any") is False
|
|
326
|
+
|
|
327
|
+
def test_repr(self, sample_context):
|
|
328
|
+
"""Test string representation."""
|
|
329
|
+
repr_str = repr(sample_context)
|
|
330
|
+
|
|
331
|
+
assert "JarvisContext" in repr_str
|
|
332
|
+
assert "test-workflow" in repr_str
|
|
333
|
+
assert "step2" in repr_str
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class TestCreateContext:
|
|
337
|
+
"""Tests for create_context factory function."""
|
|
338
|
+
|
|
339
|
+
def test_create_context_basic(self):
|
|
340
|
+
"""Test creating context with basic parameters."""
|
|
341
|
+
memory_dict = {"step0": {"output": "data"}}
|
|
342
|
+
|
|
343
|
+
ctx = create_context(
|
|
344
|
+
workflow_id="workflow-1",
|
|
345
|
+
step_id="step1",
|
|
346
|
+
task="Process",
|
|
347
|
+
params={"key": "value"},
|
|
348
|
+
memory_dict=memory_dict,
|
|
349
|
+
dependency_manager=None
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
assert ctx.workflow_id == "workflow-1"
|
|
353
|
+
assert ctx.step_id == "step1"
|
|
354
|
+
assert ctx.task == "Process"
|
|
355
|
+
assert ctx.params == {"key": "value"}
|
|
356
|
+
assert ctx.memory is not None
|
|
357
|
+
assert ctx.deps is not None
|
|
358
|
+
|
|
359
|
+
def test_create_context_memory_works(self):
|
|
360
|
+
"""Test that created context's memory accessor works."""
|
|
361
|
+
memory_dict = {"step0": {"output": [1, 2, 3]}}
|
|
362
|
+
|
|
363
|
+
ctx = create_context(
|
|
364
|
+
workflow_id="w1",
|
|
365
|
+
step_id="step1",
|
|
366
|
+
task="test",
|
|
367
|
+
params={},
|
|
368
|
+
memory_dict=memory_dict
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
assert ctx.previous("step0") == [1, 2, 3]
|
|
372
|
+
|
|
373
|
+
def test_create_context_deps_works(self):
|
|
374
|
+
"""Test that created context's deps accessor works."""
|
|
375
|
+
memory_dict = {"step0": "data", "step1": "more"}
|
|
376
|
+
|
|
377
|
+
ctx = create_context(
|
|
378
|
+
workflow_id="w1",
|
|
379
|
+
step_id="step2",
|
|
380
|
+
task="test",
|
|
381
|
+
params={},
|
|
382
|
+
memory_dict=memory_dict
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
assert ctx.deps.is_ready("step0") is True
|
|
386
|
+
assert ctx.deps.all_ready(["step0", "step1"]) is True
|
|
387
|
+
|
|
388
|
+
def test_create_context_empty_params(self):
|
|
389
|
+
"""Test creating context with empty params."""
|
|
390
|
+
ctx = create_context(
|
|
391
|
+
workflow_id="w1",
|
|
392
|
+
step_id="step0",
|
|
393
|
+
task="test",
|
|
394
|
+
params={},
|
|
395
|
+
memory_dict={}
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
assert ctx.params == {}
|
|
399
|
+
assert ctx.get_param("any") is None
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class TestMemoryAccessorEdgeCases:
|
|
403
|
+
"""Edge case tests for MemoryAccessor."""
|
|
404
|
+
|
|
405
|
+
def test_nested_output_extraction(self):
|
|
406
|
+
"""Test extraction with nested output structure."""
|
|
407
|
+
data = {
|
|
408
|
+
"step0": {
|
|
409
|
+
"status": "success",
|
|
410
|
+
"output": {
|
|
411
|
+
"nested": {
|
|
412
|
+
"deep": {"value": 42}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
memory = MemoryAccessor(data, "step1")
|
|
418
|
+
|
|
419
|
+
result = memory.get("step0")
|
|
420
|
+
assert result["nested"]["deep"]["value"] == 42
|
|
421
|
+
|
|
422
|
+
def test_list_output_extraction(self):
|
|
423
|
+
"""Test extraction when output is a list."""
|
|
424
|
+
data = {
|
|
425
|
+
"step0": {"status": "success", "output": [1, 2, 3, 4, 5]}
|
|
426
|
+
}
|
|
427
|
+
memory = MemoryAccessor(data, "step1")
|
|
428
|
+
|
|
429
|
+
result = memory.get("step0")
|
|
430
|
+
assert result == [1, 2, 3, 4, 5]
|
|
431
|
+
|
|
432
|
+
def test_none_output_extraction(self):
|
|
433
|
+
"""Test extraction when output is None."""
|
|
434
|
+
data = {
|
|
435
|
+
"step0": {"status": "success", "output": None}
|
|
436
|
+
}
|
|
437
|
+
memory = MemoryAccessor(data, "step1")
|
|
438
|
+
|
|
439
|
+
result = memory.get("step0")
|
|
440
|
+
assert result is None
|
|
441
|
+
|
|
442
|
+
def test_empty_dict_output(self):
|
|
443
|
+
"""Test extraction when output is empty dict."""
|
|
444
|
+
data = {
|
|
445
|
+
"step0": {"status": "success", "output": {}}
|
|
446
|
+
}
|
|
447
|
+
memory = MemoryAccessor(data, "step1")
|
|
448
|
+
|
|
449
|
+
result = memory.get("step0")
|
|
450
|
+
assert result == {}
|
|
451
|
+
|
|
452
|
+
def test_mutation_through_accessor(self):
|
|
453
|
+
"""Test that mutations through accessor affect original dict."""
|
|
454
|
+
data = {}
|
|
455
|
+
memory = MemoryAccessor(data, "step1")
|
|
456
|
+
|
|
457
|
+
memory.put("key", {"nested": "value"})
|
|
458
|
+
|
|
459
|
+
# Original dict should be mutated
|
|
460
|
+
assert data["key"] == {"nested": "value"}
|
|
461
|
+
|
|
462
|
+
# Modify through get and verify
|
|
463
|
+
result = memory.get("key")
|
|
464
|
+
result["new_key"] = "new_value"
|
|
465
|
+
|
|
466
|
+
# Original should reflect change (same reference)
|
|
467
|
+
assert data["key"]["new_key"] == "new_value"
|