jarviscore-framework 0.2.1__py3-none-any.whl → 0.3.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.
- examples/cloud_deployment_example.py +162 -0
- examples/customagent_cognitive_discovery_example.py +343 -0
- examples/fastapi_integration_example.py +570 -0
- jarviscore/__init__.py +19 -5
- jarviscore/cli/smoketest.py +8 -4
- jarviscore/core/agent.py +227 -0
- jarviscore/core/mesh.py +9 -0
- jarviscore/data/examples/cloud_deployment_example.py +162 -0
- jarviscore/data/examples/custom_profile_decorator.py +134 -0
- jarviscore/data/examples/custom_profile_wrap.py +168 -0
- jarviscore/data/examples/customagent_cognitive_discovery_example.py +343 -0
- jarviscore/data/examples/fastapi_integration_example.py +570 -0
- jarviscore/docs/API_REFERENCE.md +283 -3
- jarviscore/docs/CHANGELOG.md +139 -0
- jarviscore/docs/CONFIGURATION.md +1 -1
- jarviscore/docs/CUSTOMAGENT_GUIDE.md +997 -85
- jarviscore/docs/GETTING_STARTED.md +228 -267
- jarviscore/docs/TROUBLESHOOTING.md +1 -1
- jarviscore/docs/USER_GUIDE.md +153 -8
- jarviscore/integrations/__init__.py +16 -0
- jarviscore/integrations/fastapi.py +247 -0
- jarviscore/p2p/broadcaster.py +10 -3
- jarviscore/p2p/coordinator.py +310 -14
- jarviscore/p2p/keepalive.py +45 -23
- jarviscore/p2p/peer_client.py +311 -12
- jarviscore/p2p/swim_manager.py +9 -4
- jarviscore/profiles/__init__.py +7 -1
- jarviscore/profiles/customagent.py +295 -74
- {jarviscore_framework-0.2.1.dist-info → jarviscore_framework-0.3.1.dist-info}/METADATA +66 -18
- {jarviscore_framework-0.2.1.dist-info → jarviscore_framework-0.3.1.dist-info}/RECORD +37 -22
- {jarviscore_framework-0.2.1.dist-info → jarviscore_framework-0.3.1.dist-info}/WHEEL +1 -1
- tests/test_13_dx_improvements.py +554 -0
- tests/test_14_cloud_deployment.py +403 -0
- tests/test_15_llm_cognitive_discovery.py +684 -0
- tests/test_16_unified_dx_flow.py +947 -0
- {jarviscore_framework-0.2.1.dist-info → jarviscore_framework-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {jarviscore_framework-0.2.1.dist-info → jarviscore_framework-0.3.1.dist-info}/top_level.txt +0 -0
jarviscore/docs/USER_GUIDE.md
CHANGED
|
@@ -16,9 +16,12 @@ Practical guide to building agent systems with JarvisCore.
|
|
|
16
16
|
8. [Remote Sandbox](#remote-sandbox)
|
|
17
17
|
9. [Result Storage](#result-storage)
|
|
18
18
|
10. [Code Registry](#code-registry)
|
|
19
|
-
11. [
|
|
20
|
-
12. [
|
|
21
|
-
13. [
|
|
19
|
+
11. [FastAPI Integration (v0.3.0)](#fastapi-integration-v030)
|
|
20
|
+
12. [Cloud Deployment (v0.3.0)](#cloud-deployment-v030)
|
|
21
|
+
13. [Cognitive Discovery (v0.3.0)](#cognitive-discovery-v030)
|
|
22
|
+
14. [Best Practices](#best-practices)
|
|
23
|
+
15. [Common Patterns](#common-patterns)
|
|
24
|
+
16. [Troubleshooting](#troubleshooting)
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
@@ -138,12 +141,12 @@ mesh = Mesh(mode="distributed", config={'bind_port': 7950})
|
|
|
138
141
|
|
|
139
142
|
### Agents
|
|
140
143
|
|
|
141
|
-
**Agents** are workers that execute tasks. JarvisCore offers
|
|
144
|
+
**Agents** are workers that execute tasks. JarvisCore offers three profiles:
|
|
142
145
|
|
|
143
146
|
| Profile | Best For | How It Works |
|
|
144
147
|
|---------|----------|--------------|
|
|
145
148
|
| **AutoAgent** | Rapid prototyping | LLM generates + executes code from prompts |
|
|
146
|
-
| **CustomAgent** |
|
|
149
|
+
| **CustomAgent** | Your own code | Implement `on_peer_request()` for P2P or `execute_task()` for workflows |
|
|
147
150
|
|
|
148
151
|
See [AutoAgent Guide](AUTOAGENT_GUIDE.md) and [CustomAgent Guide](CUSTOMAGENT_GUIDE.md) for details.
|
|
149
152
|
|
|
@@ -223,7 +226,7 @@ asyncio.run(data_analyst_demo())
|
|
|
223
226
|
|
|
224
227
|
## Custom Profile Tutorial
|
|
225
228
|
|
|
226
|
-
|
|
229
|
+
Use **CustomAgent** profile.
|
|
227
230
|
|
|
228
231
|
See [CustomAgent Guide](CUSTOMAGENT_GUIDE.md) for:
|
|
229
232
|
- Converting standalone agents to JarvisCore
|
|
@@ -534,6 +537,148 @@ print(f"Registered: {func['registered_at']}")
|
|
|
534
537
|
|
|
535
538
|
---
|
|
536
539
|
|
|
540
|
+
## FastAPI Integration (v0.3.0)
|
|
541
|
+
|
|
542
|
+
Deploy agents as FastAPI services with minimal boilerplate:
|
|
543
|
+
|
|
544
|
+
### JarvisLifespan
|
|
545
|
+
|
|
546
|
+
```python
|
|
547
|
+
from fastapi import FastAPI, Request
|
|
548
|
+
from jarviscore.profiles import CustomAgent
|
|
549
|
+
from jarviscore.integrations.fastapi import JarvisLifespan
|
|
550
|
+
|
|
551
|
+
class ProcessorAgent(CustomAgent):
|
|
552
|
+
role = "processor"
|
|
553
|
+
capabilities = ["processing"]
|
|
554
|
+
|
|
555
|
+
async def on_peer_request(self, msg):
|
|
556
|
+
return {"result": msg.data.get("task", "").upper()}
|
|
557
|
+
|
|
558
|
+
# 3 lines to integrate
|
|
559
|
+
agent = ProcessorAgent()
|
|
560
|
+
app = FastAPI(lifespan=JarvisLifespan(agent, mode="p2p", bind_port=7950))
|
|
561
|
+
|
|
562
|
+
@app.get("/peers")
|
|
563
|
+
async def list_peers(request: Request):
|
|
564
|
+
agent = request.app.state.jarvis_agents["processor"]
|
|
565
|
+
return {"peers": agent.peers.list_peers()}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**What JarvisLifespan handles:**
|
|
569
|
+
- Mesh startup/shutdown
|
|
570
|
+
- Background task management for agent run() loops
|
|
571
|
+
- Graceful shutdown with timeouts
|
|
572
|
+
- State injection into FastAPI app
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Cloud Deployment (v0.3.0)
|
|
577
|
+
|
|
578
|
+
Deploy agents to containers without a central orchestrator:
|
|
579
|
+
|
|
580
|
+
### Self-Registration Pattern
|
|
581
|
+
|
|
582
|
+
```python
|
|
583
|
+
# In your container entrypoint
|
|
584
|
+
import asyncio
|
|
585
|
+
from jarviscore.profiles import CustomAgent
|
|
586
|
+
|
|
587
|
+
class MyAgent(CustomAgent):
|
|
588
|
+
role = "worker"
|
|
589
|
+
capabilities = ["processing"]
|
|
590
|
+
|
|
591
|
+
async def on_peer_request(self, msg):
|
|
592
|
+
return {"processed": msg.data}
|
|
593
|
+
|
|
594
|
+
async def main():
|
|
595
|
+
agent = MyAgent()
|
|
596
|
+
|
|
597
|
+
# Join existing mesh (uses JARVISCORE_SEED_NODES env var)
|
|
598
|
+
await agent.join_mesh()
|
|
599
|
+
|
|
600
|
+
print(f"Joined as {agent.role}, discovered: {agent.peers.list_peers()}")
|
|
601
|
+
|
|
602
|
+
# Run until shutdown, auto-leaves mesh on exit
|
|
603
|
+
await agent.run_standalone()
|
|
604
|
+
|
|
605
|
+
asyncio.run(main())
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### Docker/Kubernetes
|
|
609
|
+
|
|
610
|
+
```dockerfile
|
|
611
|
+
FROM python:3.11-slim
|
|
612
|
+
WORKDIR /app
|
|
613
|
+
COPY . .
|
|
614
|
+
RUN pip install jarviscore-framework
|
|
615
|
+
|
|
616
|
+
# Point to existing mesh
|
|
617
|
+
ENV JARVISCORE_SEED_NODES=mesh-service:7946
|
|
618
|
+
|
|
619
|
+
CMD ["python", "-m", "myapp.agent"]
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Environment Variables:**
|
|
623
|
+
- `JARVISCORE_SEED_NODES` - Comma-separated list of seed nodes
|
|
624
|
+
- `JARVISCORE_MESH_ENDPOINT` - Single endpoint to join
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
## Cognitive Discovery (v0.3.0)
|
|
629
|
+
|
|
630
|
+
Let LLMs dynamically discover mesh peers instead of hardcoding agent names:
|
|
631
|
+
|
|
632
|
+
### The Problem
|
|
633
|
+
|
|
634
|
+
```python
|
|
635
|
+
# Before: Hardcoded peer names in prompts
|
|
636
|
+
system_prompt = """
|
|
637
|
+
You can delegate to:
|
|
638
|
+
- analyst for data analysis
|
|
639
|
+
- scout for research
|
|
640
|
+
"""
|
|
641
|
+
# Breaks when mesh composition changes!
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### The Solution
|
|
645
|
+
|
|
646
|
+
```python
|
|
647
|
+
# After: Dynamic discovery
|
|
648
|
+
system_prompt = self.peers.build_system_prompt(
|
|
649
|
+
"You are a coordinator agent."
|
|
650
|
+
)
|
|
651
|
+
# Automatically includes all available peers!
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### get_cognitive_context()
|
|
655
|
+
|
|
656
|
+
```python
|
|
657
|
+
# Get prompt-ready peer descriptions
|
|
658
|
+
context = self.peers.get_cognitive_context(format="markdown")
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
**Output:**
|
|
662
|
+
```markdown
|
|
663
|
+
## AVAILABLE MESH PEERS
|
|
664
|
+
|
|
665
|
+
You are part of a multi-agent mesh. The following peers are available:
|
|
666
|
+
|
|
667
|
+
- **analyst** (`agent-analyst-abc123`)
|
|
668
|
+
- Capabilities: analysis, charting, reporting
|
|
669
|
+
- Description: Analyzes data and generates insights
|
|
670
|
+
|
|
671
|
+
- **scout** (`agent-scout-def456`)
|
|
672
|
+
- Capabilities: research, reconnaissance
|
|
673
|
+
- Description: Gathers information
|
|
674
|
+
|
|
675
|
+
Use the `ask_peer` tool to delegate tasks to these specialists.
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
**Formats:** `markdown`, `json`, `text`
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
537
682
|
## Best Practices
|
|
538
683
|
|
|
539
684
|
### 1. Always Use Context Managers
|
|
@@ -759,6 +904,6 @@ mesh = Mesh(config=config)
|
|
|
759
904
|
|
|
760
905
|
## Version
|
|
761
906
|
|
|
762
|
-
User Guide for JarvisCore v0.
|
|
907
|
+
User Guide for JarvisCore v0.3.1
|
|
763
908
|
|
|
764
|
-
Last Updated: 2026-
|
|
909
|
+
Last Updated: 2026-02-02
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Framework integrations for JarvisCore.
|
|
3
|
+
|
|
4
|
+
Provides first-class support for popular web frameworks,
|
|
5
|
+
reducing boilerplate for production deployments.
|
|
6
|
+
|
|
7
|
+
Available integrations:
|
|
8
|
+
- FastAPI: JarvisLifespan, create_jarvis_app
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from .fastapi import JarvisLifespan, create_jarvis_app
|
|
13
|
+
__all__ = ['JarvisLifespan', 'create_jarvis_app']
|
|
14
|
+
except ImportError:
|
|
15
|
+
# FastAPI not installed - integrations not available
|
|
16
|
+
__all__ = []
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI integration for JarvisCore.
|
|
3
|
+
|
|
4
|
+
Reduces boilerplate from ~100 lines to 3 lines for integrating
|
|
5
|
+
JarvisCore agents with FastAPI applications.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
from fastapi import FastAPI
|
|
9
|
+
from jarviscore.integrations.fastapi import JarvisLifespan
|
|
10
|
+
|
|
11
|
+
agent = MyAgent()
|
|
12
|
+
app = FastAPI(lifespan=JarvisLifespan(agent, mode="p2p", bind_port=7950))
|
|
13
|
+
|
|
14
|
+
@app.get("/peers")
|
|
15
|
+
async def get_peers(request: Request):
|
|
16
|
+
agent = request.app.state.jarvis_agents.get("my_role")
|
|
17
|
+
return {"peers": agent.peers.list_peers()}
|
|
18
|
+
"""
|
|
19
|
+
from contextlib import asynccontextmanager
|
|
20
|
+
from typing import Union, List, TYPE_CHECKING
|
|
21
|
+
import asyncio
|
|
22
|
+
import logging
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from jarviscore.core.agent import Agent
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class JarvisLifespan:
|
|
31
|
+
"""
|
|
32
|
+
FastAPI lifespan manager for JarvisCore agents.
|
|
33
|
+
|
|
34
|
+
Handles the complete lifecycle of JarvisCore mesh integration:
|
|
35
|
+
- Mesh initialization on startup
|
|
36
|
+
- Background task management for agent run() loops
|
|
37
|
+
- Graceful shutdown with proper cleanup
|
|
38
|
+
- State injection into FastAPI app for handler access
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
agents: Single agent instance or list of agents to run
|
|
42
|
+
mode: Mesh mode - "p2p", "distributed", or "autonomous"
|
|
43
|
+
**mesh_config: Additional Mesh configuration options:
|
|
44
|
+
- bind_host: P2P bind address (default: "127.0.0.1")
|
|
45
|
+
- bind_port: P2P bind port (default: 7946)
|
|
46
|
+
- seed_nodes: Comma-separated seed nodes for joining cluster
|
|
47
|
+
- node_name: Node identifier for P2P network
|
|
48
|
+
|
|
49
|
+
Example - Single Agent:
|
|
50
|
+
from fastapi import FastAPI, Request
|
|
51
|
+
from jarviscore.integrations.fastapi import JarvisLifespan
|
|
52
|
+
from jarviscore.profiles import CustomAgent
|
|
53
|
+
|
|
54
|
+
class MyAgent(CustomAgent):
|
|
55
|
+
role = "processor"
|
|
56
|
+
capabilities = ["processing"]
|
|
57
|
+
|
|
58
|
+
async def on_peer_request(self, msg):
|
|
59
|
+
return {"processed": msg.data}
|
|
60
|
+
|
|
61
|
+
agent = MyAgent()
|
|
62
|
+
app = FastAPI(lifespan=JarvisLifespan(agent, mode="p2p", bind_port=7950))
|
|
63
|
+
|
|
64
|
+
@app.get("/health")
|
|
65
|
+
async def health(request: Request):
|
|
66
|
+
mesh = request.app.state.jarvis_mesh
|
|
67
|
+
return {"status": "ok", "mesh_started": mesh._started}
|
|
68
|
+
|
|
69
|
+
Example - Multiple Agents:
|
|
70
|
+
agents = [ProcessorAgent(), AnalyzerAgent()]
|
|
71
|
+
app = FastAPI(lifespan=JarvisLifespan(agents, mode="p2p"))
|
|
72
|
+
|
|
73
|
+
Example - Joining Existing Cluster:
|
|
74
|
+
app = FastAPI(lifespan=JarvisLifespan(
|
|
75
|
+
agent,
|
|
76
|
+
mode="p2p",
|
|
77
|
+
seed_nodes="192.168.1.10:7946,192.168.1.11:7946"
|
|
78
|
+
))
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
agents: Union['Agent', List['Agent']],
|
|
84
|
+
mode: str = "p2p",
|
|
85
|
+
**mesh_config
|
|
86
|
+
):
|
|
87
|
+
"""
|
|
88
|
+
Initialize JarvisLifespan.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
agents: Single agent or list of agents to run
|
|
92
|
+
mode: Mesh mode ("p2p", "distributed", "autonomous")
|
|
93
|
+
**mesh_config: Additional Mesh configuration
|
|
94
|
+
"""
|
|
95
|
+
self.agents = agents if isinstance(agents, list) else [agents]
|
|
96
|
+
self.mode = mode
|
|
97
|
+
self.mesh_config = mesh_config
|
|
98
|
+
self.mesh = None
|
|
99
|
+
self._background_tasks: List[asyncio.Task] = []
|
|
100
|
+
self._nodes: List['Agent'] = []
|
|
101
|
+
|
|
102
|
+
@asynccontextmanager
|
|
103
|
+
async def __call__(self, app):
|
|
104
|
+
"""
|
|
105
|
+
ASGI lifespan context manager.
|
|
106
|
+
|
|
107
|
+
Called by FastAPI/Starlette on app startup/shutdown.
|
|
108
|
+
Manages the complete mesh lifecycle.
|
|
109
|
+
"""
|
|
110
|
+
from jarviscore import Mesh
|
|
111
|
+
|
|
112
|
+
# ─────────────────────────────────────────────────────────────
|
|
113
|
+
# STARTUP
|
|
114
|
+
# ─────────────────────────────────────────────────────────────
|
|
115
|
+
logger.info(f"JarvisLifespan: Starting mesh in {self.mode} mode...")
|
|
116
|
+
|
|
117
|
+
# 1. Create mesh with provided configuration
|
|
118
|
+
self.mesh = Mesh(mode=self.mode, config=self.mesh_config)
|
|
119
|
+
|
|
120
|
+
# 2. Register all agents with the mesh
|
|
121
|
+
self._nodes = []
|
|
122
|
+
for agent in self.agents:
|
|
123
|
+
node = self.mesh.add(agent)
|
|
124
|
+
self._nodes.append(node)
|
|
125
|
+
logger.debug(f"JarvisLifespan: Registered agent {node.role}")
|
|
126
|
+
|
|
127
|
+
# 3. Start mesh (initializes P2P coordinator, injects PeerClients)
|
|
128
|
+
await self.mesh.start()
|
|
129
|
+
logger.info(f"JarvisLifespan: Mesh started with {len(self._nodes)} agent(s)")
|
|
130
|
+
|
|
131
|
+
# 4. Launch agent run() loops as background tasks
|
|
132
|
+
# This is crucial - without backgrounding, the HTTP server would hang
|
|
133
|
+
for node in self._nodes:
|
|
134
|
+
if hasattr(node, 'run') and asyncio.iscoroutinefunction(node.run):
|
|
135
|
+
task = asyncio.create_task(
|
|
136
|
+
self._run_agent_with_error_handling(node),
|
|
137
|
+
name=f"jarvis-agent-{node.agent_id}"
|
|
138
|
+
)
|
|
139
|
+
self._background_tasks.append(task)
|
|
140
|
+
logger.info(f"JarvisLifespan: Started background loop for {node.role}")
|
|
141
|
+
|
|
142
|
+
# 5. Inject state into FastAPI app for handler access
|
|
143
|
+
app.state.jarvis_mesh = self.mesh
|
|
144
|
+
app.state.jarvis_agents = {node.role: node for node in self._nodes}
|
|
145
|
+
|
|
146
|
+
logger.info("JarvisLifespan: Startup complete")
|
|
147
|
+
|
|
148
|
+
# ─────────────────────────────────────────────────────────────
|
|
149
|
+
# APP RUNS HERE
|
|
150
|
+
# ─────────────────────────────────────────────────────────────
|
|
151
|
+
try:
|
|
152
|
+
yield
|
|
153
|
+
finally:
|
|
154
|
+
# ─────────────────────────────────────────────────────────────
|
|
155
|
+
# SHUTDOWN
|
|
156
|
+
# ─────────────────────────────────────────────────────────────
|
|
157
|
+
logger.info("JarvisLifespan: Shutting down...")
|
|
158
|
+
|
|
159
|
+
# 1. Request shutdown for all agents (signals run() loops to exit)
|
|
160
|
+
for node in self._nodes:
|
|
161
|
+
node.request_shutdown()
|
|
162
|
+
|
|
163
|
+
# 2. Cancel background tasks gracefully with timeout
|
|
164
|
+
for task in self._background_tasks:
|
|
165
|
+
if not task.done():
|
|
166
|
+
task.cancel()
|
|
167
|
+
try:
|
|
168
|
+
await asyncio.wait_for(task, timeout=5.0)
|
|
169
|
+
except (asyncio.CancelledError, asyncio.TimeoutError):
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
# 3. Stop mesh (cleanup P2P coordinator, call agent teardown)
|
|
173
|
+
if self.mesh:
|
|
174
|
+
await self.mesh.stop()
|
|
175
|
+
|
|
176
|
+
logger.info("JarvisLifespan: Shutdown complete")
|
|
177
|
+
|
|
178
|
+
async def _run_agent_with_error_handling(self, agent: 'Agent'):
|
|
179
|
+
"""
|
|
180
|
+
Run agent loop with error handling and logging.
|
|
181
|
+
|
|
182
|
+
Wraps the agent's run() method to catch and log errors
|
|
183
|
+
without crashing the entire application.
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
await agent.run()
|
|
187
|
+
except asyncio.CancelledError:
|
|
188
|
+
logger.debug(f"Agent {agent.agent_id} loop cancelled")
|
|
189
|
+
raise
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.error(f"Agent {agent.agent_id} loop error: {e}", exc_info=True)
|
|
192
|
+
# Re-raise to allow task to be marked as failed
|
|
193
|
+
raise
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def create_jarvis_app(
|
|
197
|
+
agent: 'Agent',
|
|
198
|
+
mode: str = "p2p",
|
|
199
|
+
title: str = "JarvisCore Agent",
|
|
200
|
+
description: str = "API powered by JarvisCore",
|
|
201
|
+
version: str = "1.0.0",
|
|
202
|
+
**mesh_config
|
|
203
|
+
) -> 'FastAPI':
|
|
204
|
+
"""
|
|
205
|
+
Create a FastAPI app with JarvisCore integration pre-configured.
|
|
206
|
+
|
|
207
|
+
Convenience function for simple single-agent deployments.
|
|
208
|
+
For more control, use JarvisLifespan directly.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
agent: Agent instance to run
|
|
212
|
+
mode: Mesh mode ("p2p", "distributed", "autonomous")
|
|
213
|
+
title: FastAPI app title
|
|
214
|
+
description: FastAPI app description
|
|
215
|
+
version: API version
|
|
216
|
+
**mesh_config: Mesh configuration options
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Configured FastAPI app with JarvisCore integration
|
|
220
|
+
|
|
221
|
+
Example:
|
|
222
|
+
from jarviscore.integrations.fastapi import create_jarvis_app
|
|
223
|
+
from jarviscore.profiles import CustomAgent
|
|
224
|
+
|
|
225
|
+
class MyAgent(CustomAgent):
|
|
226
|
+
role = "processor"
|
|
227
|
+
capabilities = ["processing"]
|
|
228
|
+
|
|
229
|
+
async def on_peer_request(self, msg):
|
|
230
|
+
return {"result": "processed"}
|
|
231
|
+
|
|
232
|
+
app = create_jarvis_app(MyAgent(), mode="p2p", bind_port=7950)
|
|
233
|
+
|
|
234
|
+
@app.get("/health")
|
|
235
|
+
async def health():
|
|
236
|
+
return {"status": "ok"}
|
|
237
|
+
|
|
238
|
+
# Run with: uvicorn myapp:app --host 0.0.0.0 --port 8000
|
|
239
|
+
"""
|
|
240
|
+
from fastapi import FastAPI
|
|
241
|
+
|
|
242
|
+
return FastAPI(
|
|
243
|
+
title=title,
|
|
244
|
+
description=description,
|
|
245
|
+
version=version,
|
|
246
|
+
lifespan=JarvisLifespan(agent, mode=mode, **mesh_config)
|
|
247
|
+
)
|
jarviscore/p2p/broadcaster.py
CHANGED
|
@@ -143,12 +143,19 @@ class StepOutputBroadcaster:
|
|
|
143
143
|
"""
|
|
144
144
|
try:
|
|
145
145
|
message_id = message_data.get('id', str(uuid.uuid4()))
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
|
|
147
|
+
# The sender uses 'step_output_data' as the key (json string)
|
|
148
|
+
step_output_json = message_data.get('step_output_data', '')
|
|
149
|
+
if step_output_json:
|
|
150
|
+
step_result_data = json.loads(step_output_json)
|
|
151
|
+
else:
|
|
152
|
+
# Fallback for legacy format
|
|
153
|
+
step_result_data = message_data.get('step_result', {})
|
|
154
|
+
|
|
148
155
|
# Send acknowledgment if callback provided
|
|
149
156
|
if send_ack_callback:
|
|
150
157
|
await send_ack_callback(sender_swim_id, message_id, "STEP_OUTPUT_BROADCAST")
|
|
151
|
-
|
|
158
|
+
|
|
152
159
|
# Create StepExecutionResult from received data
|
|
153
160
|
result = StepExecutionResult(**step_result_data)
|
|
154
161
|
|