jarviscore-framework 0.1.0__py3-none-any.whl → 0.2.0__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.
- examples/autoagent_distributed_example.py +211 -0
- examples/custom_profile_decorator.py +134 -0
- examples/custom_profile_wrap.py +168 -0
- examples/customagent_distributed_example.py +362 -0
- examples/customagent_p2p_example.py +347 -0
- jarviscore/__init__.py +60 -15
- jarviscore/adapter/__init__.py +40 -0
- jarviscore/adapter/decorator.py +336 -0
- jarviscore/adapter/wrapper.py +303 -0
- jarviscore/cli/check.py +18 -13
- jarviscore/cli/scaffold.py +178 -0
- jarviscore/cli/smoketest.py +3 -2
- 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/core/agent.py +44 -1
- jarviscore/core/mesh.py +196 -35
- jarviscore/data/.env.example +146 -0
- jarviscore/data/__init__.py +7 -0
- jarviscore/data/examples/autoagent_distributed_example.py +211 -0
- jarviscore/data/examples/calculator_agent_example.py +77 -0
- jarviscore/data/examples/customagent_distributed_example.py +362 -0
- jarviscore/data/examples/customagent_p2p_example.py +347 -0
- jarviscore/data/examples/multi_agent_workflow.py +132 -0
- jarviscore/data/examples/research_agent_example.py +76 -0
- jarviscore/docs/API_REFERENCE.md +264 -51
- jarviscore/docs/AUTOAGENT_GUIDE.md +198 -0
- jarviscore/docs/CONFIGURATION.md +41 -23
- jarviscore/docs/CUSTOMAGENT_GUIDE.md +415 -0
- jarviscore/docs/GETTING_STARTED.md +113 -17
- jarviscore/docs/TROUBLESHOOTING.md +155 -13
- jarviscore/docs/USER_GUIDE.md +144 -363
- jarviscore/execution/llm.py +23 -16
- jarviscore/orchestration/engine.py +20 -8
- jarviscore/p2p/__init__.py +10 -0
- jarviscore/p2p/coordinator.py +129 -0
- jarviscore/p2p/messages.py +87 -0
- jarviscore/p2p/peer_client.py +576 -0
- jarviscore/p2p/peer_tool.py +268 -0
- jarviscore_framework-0.2.0.dist-info/METADATA +143 -0
- jarviscore_framework-0.2.0.dist-info/RECORD +132 -0
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/WHEEL +1 -1
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/top_level.txt +1 -0
- test_logs/code_registry/functions/data_generator-558779ed_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-5ed3609e_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-66da0356_43970bb9.py +25 -0
- test_logs/code_registry/functions/data_generator-7a2fac83_583709d9.py +36 -0
- test_logs/code_registry/functions/data_generator-888b670f_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-9ca5f642_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-bfd90775_560ebc37.py +7 -0
- test_logs/code_registry/functions/data_generator-e95d2f7d_aa235863.py +9 -0
- test_logs/code_registry/functions/data_generator-f60ca8a2_327eb8c2.py +29 -0
- test_logs/code_registry/functions/mathematician-02adf9ee_958658d9.py +19 -0
- test_logs/code_registry/functions/mathematician-0706fb57_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-153c9c4a_ba59c918.py +83 -0
- test_logs/code_registry/functions/mathematician-287e61c0_41daa793.py +18 -0
- test_logs/code_registry/functions/mathematician-2967af5a_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-303ca6d6_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-308a4afd_cbf5064d.py +73 -0
- test_logs/code_registry/functions/mathematician-353f16e2_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-3c22475a_41daa793.py +17 -0
- test_logs/code_registry/functions/mathematician-5bac1029_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-640f76b2_9198780b.py +19 -0
- test_logs/code_registry/functions/mathematician-752fa7ea_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-baf9ef39_0968bcf5.py +18 -0
- test_logs/code_registry/functions/mathematician-bc8b2a2f_5df13441.py +23 -0
- test_logs/code_registry/functions/mathematician-c31e4686_41daa793.py +18 -0
- test_logs/code_registry/functions/mathematician-cc84c84c_863c2cc6.py +17 -0
- test_logs/code_registry/functions/mathematician-dd7c7144_9198780b.py +19 -0
- test_logs/code_registry/functions/mathematician-e671c256_41ea4487.py +74 -0
- test_logs/code_registry/functions/report_generator-1a878fcc_18d44bdc.py +47 -0
- test_logs/code_registry/functions/report_generator-25c1c331_cea57d0d.py +35 -0
- test_logs/code_registry/functions/report_generator-37552117_e711c2b9.py +35 -0
- test_logs/code_registry/functions/report_generator-bc662768_e711c2b9.py +35 -0
- test_logs/code_registry/functions/report_generator-d6c0e76b_5e7722ec.py +44 -0
- test_logs/code_registry/functions/report_generator-f270fb02_680529c3.py +44 -0
- test_logs/code_registry/functions/text_processor-11393b14_4370d3ed.py +40 -0
- test_logs/code_registry/functions/text_processor-7d02dfc3_d3b569be.py +37 -0
- test_logs/code_registry/functions/text_processor-8adb5e32_9168c5fe.py +13 -0
- test_logs/code_registry/functions/text_processor-c58ffc19_78b4ceac.py +42 -0
- test_logs/code_registry/functions/text_processor-cd5977b1_9168c5fe.py +13 -0
- test_logs/code_registry/functions/text_processor-ec1c8773_9168c5fe.py +13 -0
- tests/test_01_analyst_standalone.py +124 -0
- tests/test_02_assistant_standalone.py +164 -0
- tests/test_03_analyst_with_framework.py +945 -0
- tests/test_04_assistant_with_framework.py +1002 -0
- tests/test_05_integration.py +1301 -0
- tests/test_06_real_llm_integration.py +760 -0
- tests/test_07_distributed_single_node.py +578 -0
- tests/test_08_distributed_multi_node.py +454 -0
- tests/test_09_distributed_autoagent.py +509 -0
- tests/test_10_distributed_customagent.py +787 -0
- tests/test_context.py +467 -0
- tests/test_decorator.py +622 -0
- tests/test_mesh.py +35 -4
- jarviscore_framework-0.1.0.dist-info/METADATA +0 -136
- jarviscore_framework-0.1.0.dist-info/RECORD +0 -55
- {jarviscore_framework-0.1.0.dist-info → jarviscore_framework-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JarvisCore Project Initialization CLI
|
|
3
|
+
|
|
4
|
+
Scaffolds a new JarvisCore project with configuration and examples.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python -m jarviscore.cli.scaffold # Create .env from template
|
|
8
|
+
python -m jarviscore.cli.scaffold --examples # Also copy example files
|
|
9
|
+
python -m jarviscore.cli.scaffold --force # Overwrite existing files
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import shutil
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from importlib import resources
|
|
16
|
+
import argparse
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_data_path() -> Path:
|
|
20
|
+
"""Get path to the data directory within the package."""
|
|
21
|
+
# Python 3.9+ approach using importlib.resources
|
|
22
|
+
try:
|
|
23
|
+
# resources.files() returns a Traversable, convert to Path
|
|
24
|
+
data_path = resources.files('jarviscore.data')
|
|
25
|
+
return Path(str(data_path))
|
|
26
|
+
except (TypeError, AttributeError):
|
|
27
|
+
# Fallback for older Python or if resources.files doesn't work
|
|
28
|
+
import jarviscore.data
|
|
29
|
+
return Path(jarviscore.data.__file__).parent
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def copy_env_example(dest_dir: Path, force: bool = False) -> bool:
|
|
33
|
+
"""
|
|
34
|
+
Copy .env.example to destination directory.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
dest_dir: Destination directory
|
|
38
|
+
force: Overwrite if exists
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
True if copied, False if skipped
|
|
42
|
+
"""
|
|
43
|
+
data_path = get_data_path()
|
|
44
|
+
src = data_path / '.env.example'
|
|
45
|
+
dest = dest_dir / '.env.example'
|
|
46
|
+
|
|
47
|
+
if not src.exists():
|
|
48
|
+
print(f"✗ Source file not found: {src}")
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
if dest.exists() and not force:
|
|
52
|
+
print(f"⚠ {dest.name} already exists (use --force to overwrite)")
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
shutil.copy2(src, dest)
|
|
56
|
+
print(f"✓ Created {dest.name}")
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def copy_examples(dest_dir: Path, force: bool = False) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Copy example files to destination directory.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
dest_dir: Destination directory
|
|
66
|
+
force: Overwrite if exists
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
True if copied, False if skipped
|
|
70
|
+
"""
|
|
71
|
+
data_path = get_data_path()
|
|
72
|
+
src = data_path / 'examples'
|
|
73
|
+
dest = dest_dir / 'examples'
|
|
74
|
+
|
|
75
|
+
if not src.exists():
|
|
76
|
+
print(f"✗ Examples directory not found: {src}")
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
if dest.exists() and not force:
|
|
80
|
+
print(f"⚠ examples/ directory already exists (use --force to overwrite)")
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
if dest.exists() and force:
|
|
84
|
+
shutil.rmtree(dest)
|
|
85
|
+
|
|
86
|
+
shutil.copytree(src, dest)
|
|
87
|
+
|
|
88
|
+
# Count files copied
|
|
89
|
+
file_count = sum(1 for _ in dest.glob('*.py'))
|
|
90
|
+
print(f"✓ Created examples/ directory ({file_count} files)")
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def print_header():
|
|
95
|
+
"""Print initialization header."""
|
|
96
|
+
print("\n" + "=" * 60)
|
|
97
|
+
print(" JarvisCore Project Initialization")
|
|
98
|
+
print("=" * 60 + "\n")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def print_next_steps(env_created: bool, examples_created: bool):
|
|
102
|
+
"""Print next steps after initialization."""
|
|
103
|
+
print("\n" + "=" * 60)
|
|
104
|
+
print(" Next Steps")
|
|
105
|
+
print("=" * 60)
|
|
106
|
+
|
|
107
|
+
steps = []
|
|
108
|
+
|
|
109
|
+
if env_created:
|
|
110
|
+
steps.append("1. Copy and configure your environment:\n"
|
|
111
|
+
" cp .env.example .env\n"
|
|
112
|
+
" # Edit .env and add your LLM API key")
|
|
113
|
+
|
|
114
|
+
steps.append(f"{'2' if env_created else '1'}. Validate your setup:\n"
|
|
115
|
+
" python -m jarviscore.cli.check --validate-llm")
|
|
116
|
+
|
|
117
|
+
steps.append(f"{'3' if env_created else '2'}. Run smoke test:\n"
|
|
118
|
+
" python -m jarviscore.cli.smoketest")
|
|
119
|
+
|
|
120
|
+
if examples_created:
|
|
121
|
+
steps.append(f"{'4' if env_created else '3'}. Try an example:\n"
|
|
122
|
+
" python examples/calculator_agent_example.py for AutoAgent Profile or python examples/customagent_p2p_example.py ")
|
|
123
|
+
|
|
124
|
+
for step in steps:
|
|
125
|
+
print(f"\n{step}")
|
|
126
|
+
|
|
127
|
+
print()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main():
|
|
131
|
+
"""CLI entry point."""
|
|
132
|
+
parser = argparse.ArgumentParser(
|
|
133
|
+
description='Initialize a new JarvisCore project'
|
|
134
|
+
)
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
'--examples',
|
|
137
|
+
action='store_true',
|
|
138
|
+
help='Also copy example agent files'
|
|
139
|
+
)
|
|
140
|
+
parser.add_argument(
|
|
141
|
+
'--force',
|
|
142
|
+
action='store_true',
|
|
143
|
+
help='Overwrite existing files'
|
|
144
|
+
)
|
|
145
|
+
parser.add_argument(
|
|
146
|
+
'--dir',
|
|
147
|
+
type=str,
|
|
148
|
+
default='.',
|
|
149
|
+
help='Target directory (default: current directory)'
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
args = parser.parse_args()
|
|
153
|
+
dest_dir = Path(args.dir).resolve()
|
|
154
|
+
|
|
155
|
+
print_header()
|
|
156
|
+
print(f"Initializing in: {dest_dir}\n")
|
|
157
|
+
|
|
158
|
+
# Ensure destination exists
|
|
159
|
+
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
160
|
+
|
|
161
|
+
# Copy files
|
|
162
|
+
env_created = copy_env_example(dest_dir, args.force)
|
|
163
|
+
|
|
164
|
+
examples_created = False
|
|
165
|
+
if args.examples:
|
|
166
|
+
examples_created = copy_examples(dest_dir, args.force)
|
|
167
|
+
|
|
168
|
+
# Summary
|
|
169
|
+
if env_created or examples_created:
|
|
170
|
+
print_next_steps(env_created, examples_created)
|
|
171
|
+
sys.exit(0)
|
|
172
|
+
else:
|
|
173
|
+
print("\n⚠ No files were created. Use --force to overwrite existing files.")
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == '__main__':
|
|
178
|
+
main()
|
jarviscore/cli/smoketest.py
CHANGED
|
@@ -311,8 +311,9 @@ class SmokeTest:
|
|
|
311
311
|
|
|
312
312
|
print("\n✓ All smoke tests passed!")
|
|
313
313
|
print("\nJarvisCore is working correctly. Next steps:")
|
|
314
|
-
print(" 1. Try examples: python examples/calculator_agent_example.py")
|
|
315
|
-
print(" 2.
|
|
314
|
+
print(" 1. Try examples - AutoAgent Profile: python examples/calculator_agent_example.py")
|
|
315
|
+
print(" 2. Try examples - CustomAgent Profile: python examples/customagent_p2p_example.py")
|
|
316
|
+
print(" 3. Read user guide: docs/USER_GUIDE.md")
|
|
316
317
|
print(" 3. Build your first agent: docs/GETTING_STARTED.md")
|
|
317
318
|
print()
|
|
318
319
|
return True
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Context module for JarvisCore Custom Profile.
|
|
3
|
+
|
|
4
|
+
Provides orchestration primitives for wrapped agents:
|
|
5
|
+
- JarvisContext: Unified context with workflow info and accessors
|
|
6
|
+
- MemoryAccessor: Clean API over workflow memory
|
|
7
|
+
- DependencyAccessor: Clean API over dependency management
|
|
8
|
+
|
|
9
|
+
These are facades over existing JarvisCore components, providing
|
|
10
|
+
a developer-friendly interface for Custom Profile agents.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
from jarviscore.context import JarvisContext
|
|
14
|
+
|
|
15
|
+
@jarvis_agent(role="processor", capabilities=["processing"])
|
|
16
|
+
class Processor:
|
|
17
|
+
def run(self, task, ctx: JarvisContext):
|
|
18
|
+
# Access previous step
|
|
19
|
+
data = ctx.previous("step1")
|
|
20
|
+
|
|
21
|
+
# Access memory
|
|
22
|
+
all_data = ctx.memory.all()
|
|
23
|
+
|
|
24
|
+
# Check dependencies
|
|
25
|
+
if ctx.deps.is_ready("optional"):
|
|
26
|
+
optional = ctx.previous("optional")
|
|
27
|
+
|
|
28
|
+
return {"processed": process(data)}
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from .jarvis_context import JarvisContext, create_context
|
|
32
|
+
from .memory import MemoryAccessor
|
|
33
|
+
from .dependency import DependencyAccessor
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
'JarvisContext',
|
|
37
|
+
'create_context',
|
|
38
|
+
'MemoryAccessor',
|
|
39
|
+
'DependencyAccessor',
|
|
40
|
+
]
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DependencyAccessor - Clean API over DependencyManager.
|
|
3
|
+
|
|
4
|
+
Wraps the existing orchestration.DependencyManager to provide
|
|
5
|
+
a developer-friendly interface for Custom Profile agents.
|
|
6
|
+
"""
|
|
7
|
+
from typing import List, Dict, Any, Tuple, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DependencyAccessor:
|
|
11
|
+
"""
|
|
12
|
+
Provides clean access to dependency management.
|
|
13
|
+
|
|
14
|
+
This is a facade over the existing DependencyManager class.
|
|
15
|
+
It provides a simpler interface for checking and waiting on dependencies.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
# In agent's run method with ctx: JarvisContext
|
|
19
|
+
await ctx.deps.wait_for(["step1", "step2"])
|
|
20
|
+
ready, missing = ctx.deps.check(["step1", "step2"])
|
|
21
|
+
if ctx.deps.is_ready("optional_step"):
|
|
22
|
+
...
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
dependency_manager: Optional[Any],
|
|
28
|
+
memory: Dict[str, Any]
|
|
29
|
+
):
|
|
30
|
+
"""
|
|
31
|
+
Initialize dependency accessor.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
dependency_manager: Reference to orchestration.DependencyManager
|
|
35
|
+
memory: Reference to WorkflowEngine.memory dict
|
|
36
|
+
"""
|
|
37
|
+
self._manager = dependency_manager
|
|
38
|
+
self._memory = memory
|
|
39
|
+
|
|
40
|
+
async def wait_for(
|
|
41
|
+
self,
|
|
42
|
+
step_ids: List[str],
|
|
43
|
+
timeout: float = 300.0
|
|
44
|
+
) -> Dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
Wait for specific steps to complete.
|
|
47
|
+
|
|
48
|
+
Blocks until all specified steps have outputs in memory.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
step_ids: List of step IDs to wait for
|
|
52
|
+
timeout: Maximum wait time in seconds (default: 5 minutes)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dictionary of step_id -> output
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
TimeoutError: If dependencies not ready within timeout
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
results = await deps.wait_for(["step1", "step2"])
|
|
62
|
+
step1_data = results["step1"]
|
|
63
|
+
"""
|
|
64
|
+
if self._manager is None:
|
|
65
|
+
# Fallback: simple check without manager
|
|
66
|
+
return self._simple_wait(step_ids)
|
|
67
|
+
|
|
68
|
+
return await self._manager.wait_for(step_ids, self._memory, timeout)
|
|
69
|
+
|
|
70
|
+
def _simple_wait(self, step_ids: List[str]) -> Dict[str, Any]:
|
|
71
|
+
"""
|
|
72
|
+
Simple synchronous check (used when manager not available).
|
|
73
|
+
|
|
74
|
+
Returns outputs for steps that exist in memory.
|
|
75
|
+
"""
|
|
76
|
+
result = {}
|
|
77
|
+
for step_id in step_ids:
|
|
78
|
+
if step_id in self._memory:
|
|
79
|
+
value = self._memory[step_id]
|
|
80
|
+
if isinstance(value, dict) and 'output' in value:
|
|
81
|
+
result[step_id] = value['output']
|
|
82
|
+
else:
|
|
83
|
+
result[step_id] = value
|
|
84
|
+
return result
|
|
85
|
+
|
|
86
|
+
def check(self, step_ids: List[str]) -> Tuple[bool, List[str]]:
|
|
87
|
+
"""
|
|
88
|
+
Check if dependencies are satisfied (non-blocking).
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
step_ids: Steps to check
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Tuple of (all_satisfied, missing_step_ids)
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
ready, missing = deps.check(["step1", "step2"])
|
|
98
|
+
if not ready:
|
|
99
|
+
print(f"Still waiting for: {missing}")
|
|
100
|
+
"""
|
|
101
|
+
if self._manager is None:
|
|
102
|
+
# Fallback: simple check without manager
|
|
103
|
+
missing = [s for s in step_ids if s not in self._memory]
|
|
104
|
+
return (len(missing) == 0, missing)
|
|
105
|
+
|
|
106
|
+
return self._manager.check_dependencies(step_ids, self._memory)
|
|
107
|
+
|
|
108
|
+
def is_ready(self, step_id: str) -> bool:
|
|
109
|
+
"""
|
|
110
|
+
Check if a single step is ready (non-blocking).
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
step_id: Step to check
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
True if step output exists in memory
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
if deps.is_ready("optional_step"):
|
|
120
|
+
data = ctx.memory.get("optional_step")
|
|
121
|
+
"""
|
|
122
|
+
return step_id in self._memory
|
|
123
|
+
|
|
124
|
+
def all_ready(self, step_ids: List[str]) -> bool:
|
|
125
|
+
"""
|
|
126
|
+
Check if all specified steps are ready.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
step_ids: Steps to check
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
True if all steps have outputs in memory
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
if deps.all_ready(["step1", "step2", "step3"]):
|
|
136
|
+
# All dependencies satisfied
|
|
137
|
+
...
|
|
138
|
+
"""
|
|
139
|
+
return all(self.is_ready(s) for s in step_ids)
|
|
140
|
+
|
|
141
|
+
def any_ready(self, step_ids: List[str]) -> bool:
|
|
142
|
+
"""
|
|
143
|
+
Check if any of the specified steps are ready.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
step_ids: Steps to check
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
True if at least one step has output in memory
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
if deps.any_ready(["cache_step", "compute_step"]):
|
|
153
|
+
# At least one source available
|
|
154
|
+
...
|
|
155
|
+
"""
|
|
156
|
+
return any(self.is_ready(s) for s in step_ids)
|
|
157
|
+
|
|
158
|
+
def __repr__(self) -> str:
|
|
159
|
+
ready_count = sum(1 for k in self._memory.keys())
|
|
160
|
+
return f"<DependencyAccessor ready_steps={ready_count}>"
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JarvisContext - Unified context for Custom Profile agents.
|
|
3
|
+
|
|
4
|
+
Provides a single object that gives agents access to:
|
|
5
|
+
- Workflow information (workflow_id, step_id)
|
|
6
|
+
- Task information (task description, params)
|
|
7
|
+
- Memory (shared state between steps)
|
|
8
|
+
- Dependencies (check/wait for other steps)
|
|
9
|
+
|
|
10
|
+
This is a facade over existing JarvisCore primitives.
|
|
11
|
+
"""
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Dict, Any, Optional, List
|
|
14
|
+
|
|
15
|
+
from .memory import MemoryAccessor
|
|
16
|
+
from .dependency import DependencyAccessor
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class JarvisContext:
|
|
21
|
+
"""
|
|
22
|
+
Context passed to wrapped agents during execution.
|
|
23
|
+
|
|
24
|
+
Provides unified access to JarvisCore orchestration primitives.
|
|
25
|
+
Agents receive this as the `ctx` parameter when they declare it
|
|
26
|
+
in their run method signature.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
workflow_id: Unique identifier for the current workflow
|
|
30
|
+
step_id: Unique identifier for the current step
|
|
31
|
+
task: Task description string
|
|
32
|
+
params: Task parameters dictionary
|
|
33
|
+
memory: Accessor for shared workflow memory
|
|
34
|
+
deps: Accessor for dependency management
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
@jarvis_agent(role="aggregator", capabilities=["aggregation"])
|
|
38
|
+
class Aggregator:
|
|
39
|
+
def run(self, task, ctx: JarvisContext):
|
|
40
|
+
# Access previous step output
|
|
41
|
+
step1_data = ctx.previous("step1")
|
|
42
|
+
|
|
43
|
+
# Access all previous results
|
|
44
|
+
all_results = ctx.memory.all()
|
|
45
|
+
|
|
46
|
+
# Check workflow info
|
|
47
|
+
print(f"Running step {ctx.step_id} in {ctx.workflow_id}")
|
|
48
|
+
|
|
49
|
+
# Use params
|
|
50
|
+
threshold = ctx.params.get("threshold", 0.5)
|
|
51
|
+
|
|
52
|
+
return {"aggregated": process(step1_data)}
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# Workflow info
|
|
56
|
+
workflow_id: str
|
|
57
|
+
step_id: str
|
|
58
|
+
|
|
59
|
+
# Task info
|
|
60
|
+
task: str
|
|
61
|
+
params: Dict[str, Any] = field(default_factory=dict)
|
|
62
|
+
|
|
63
|
+
# Orchestration accessors
|
|
64
|
+
memory: MemoryAccessor = None
|
|
65
|
+
deps: DependencyAccessor = None
|
|
66
|
+
|
|
67
|
+
def previous(self, step_id: str, default: Any = None) -> Any:
|
|
68
|
+
"""
|
|
69
|
+
Get output from a previous step.
|
|
70
|
+
|
|
71
|
+
Convenience method that delegates to memory.get().
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
step_id: ID of the step to get output from
|
|
75
|
+
default: Default value if not found
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Step output or default
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
step1_result = ctx.previous("step1")
|
|
82
|
+
optional = ctx.previous("optional_step", default={})
|
|
83
|
+
"""
|
|
84
|
+
if self.memory is None:
|
|
85
|
+
return default
|
|
86
|
+
return self.memory.get(step_id, default)
|
|
87
|
+
|
|
88
|
+
def all_previous(self) -> Dict[str, Any]:
|
|
89
|
+
"""
|
|
90
|
+
Get all previous step outputs.
|
|
91
|
+
|
|
92
|
+
Convenience method that delegates to memory.all().
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Dictionary of step_id -> output
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
all_results = ctx.all_previous()
|
|
99
|
+
for step_id, output in all_results.items():
|
|
100
|
+
print(f"{step_id} produced: {output}")
|
|
101
|
+
"""
|
|
102
|
+
if self.memory is None:
|
|
103
|
+
return {}
|
|
104
|
+
return self.memory.all()
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def previous_results(self) -> Dict[str, Any]:
|
|
108
|
+
"""
|
|
109
|
+
Alias for all_previous().
|
|
110
|
+
|
|
111
|
+
Provides property-style access to all previous results.
|
|
112
|
+
|
|
113
|
+
Example:
|
|
114
|
+
results = ctx.previous_results
|
|
115
|
+
"""
|
|
116
|
+
return self.all_previous()
|
|
117
|
+
|
|
118
|
+
def has_previous(self, step_id: str) -> bool:
|
|
119
|
+
"""
|
|
120
|
+
Check if a previous step's output exists.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
step_id: Step to check
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
True if output exists
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
if ctx.has_previous("optional_step"):
|
|
130
|
+
data = ctx.previous("optional_step")
|
|
131
|
+
"""
|
|
132
|
+
if self.memory is None:
|
|
133
|
+
return False
|
|
134
|
+
return self.memory.has(step_id)
|
|
135
|
+
|
|
136
|
+
def get_param(self, key: str, default: Any = None) -> Any:
|
|
137
|
+
"""
|
|
138
|
+
Get a task parameter by key.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
key: Parameter key
|
|
142
|
+
default: Default value if not found
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Parameter value or default
|
|
146
|
+
|
|
147
|
+
Example:
|
|
148
|
+
threshold = ctx.get_param("threshold", 0.5)
|
|
149
|
+
mode = ctx.get_param("mode", "default")
|
|
150
|
+
"""
|
|
151
|
+
return self.params.get(key, default)
|
|
152
|
+
|
|
153
|
+
def __repr__(self) -> str:
|
|
154
|
+
return (
|
|
155
|
+
f"<JarvisContext "
|
|
156
|
+
f"workflow={self.workflow_id} "
|
|
157
|
+
f"step={self.step_id} "
|
|
158
|
+
f"params={list(self.params.keys())}>"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def create_context(
|
|
163
|
+
workflow_id: str,
|
|
164
|
+
step_id: str,
|
|
165
|
+
task: str,
|
|
166
|
+
params: Dict[str, Any],
|
|
167
|
+
memory_dict: Dict[str, Any],
|
|
168
|
+
dependency_manager: Optional[Any] = None
|
|
169
|
+
) -> JarvisContext:
|
|
170
|
+
"""
|
|
171
|
+
Factory function to create a JarvisContext.
|
|
172
|
+
|
|
173
|
+
Used internally by the decorator and WorkflowEngine to create
|
|
174
|
+
context objects for agents.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
workflow_id: Workflow identifier
|
|
178
|
+
step_id: Step identifier
|
|
179
|
+
task: Task description
|
|
180
|
+
params: Task parameters
|
|
181
|
+
memory_dict: Reference to WorkflowEngine.memory
|
|
182
|
+
dependency_manager: Optional reference to DependencyManager
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Configured JarvisContext instance
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
ctx = create_context(
|
|
189
|
+
workflow_id="pipeline-1",
|
|
190
|
+
step_id="step2",
|
|
191
|
+
task="Process data",
|
|
192
|
+
params={"threshold": 0.5},
|
|
193
|
+
memory_dict=engine.memory,
|
|
194
|
+
dependency_manager=engine.dependency_manager
|
|
195
|
+
)
|
|
196
|
+
"""
|
|
197
|
+
memory_accessor = MemoryAccessor(memory_dict, step_id)
|
|
198
|
+
dep_accessor = DependencyAccessor(dependency_manager, memory_dict)
|
|
199
|
+
|
|
200
|
+
return JarvisContext(
|
|
201
|
+
workflow_id=workflow_id,
|
|
202
|
+
step_id=step_id,
|
|
203
|
+
task=task,
|
|
204
|
+
params=params,
|
|
205
|
+
memory=memory_accessor,
|
|
206
|
+
deps=dep_accessor
|
|
207
|
+
)
|