jarviscore-framework 0.3.0__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.
Files changed (31) hide show
  1. examples/cloud_deployment_example.py +3 -3
  2. examples/{listeneragent_cognitive_discovery_example.py → customagent_cognitive_discovery_example.py} +6 -6
  3. examples/fastapi_integration_example.py +4 -4
  4. jarviscore/__init__.py +8 -11
  5. jarviscore/cli/smoketest.py +1 -1
  6. jarviscore/core/mesh.py +9 -0
  7. jarviscore/data/examples/cloud_deployment_example.py +3 -3
  8. jarviscore/data/examples/custom_profile_decorator.py +134 -0
  9. jarviscore/data/examples/custom_profile_wrap.py +168 -0
  10. jarviscore/data/examples/{listeneragent_cognitive_discovery_example.py → customagent_cognitive_discovery_example.py} +6 -6
  11. jarviscore/data/examples/fastapi_integration_example.py +4 -4
  12. jarviscore/docs/API_REFERENCE.md +32 -45
  13. jarviscore/docs/CHANGELOG.md +42 -0
  14. jarviscore/docs/CONFIGURATION.md +1 -1
  15. jarviscore/docs/CUSTOMAGENT_GUIDE.md +246 -153
  16. jarviscore/docs/GETTING_STARTED.md +186 -329
  17. jarviscore/docs/TROUBLESHOOTING.md +1 -1
  18. jarviscore/docs/USER_GUIDE.md +8 -9
  19. jarviscore/integrations/fastapi.py +4 -4
  20. jarviscore/p2p/peer_client.py +29 -2
  21. jarviscore/profiles/__init__.py +2 -4
  22. jarviscore/profiles/customagent.py +295 -74
  23. {jarviscore_framework-0.3.0.dist-info → jarviscore_framework-0.3.1.dist-info}/METADATA +61 -46
  24. {jarviscore_framework-0.3.0.dist-info → jarviscore_framework-0.3.1.dist-info}/RECORD +30 -29
  25. tests/test_13_dx_improvements.py +37 -37
  26. tests/test_15_llm_cognitive_discovery.py +18 -18
  27. tests/test_16_unified_dx_flow.py +3 -3
  28. jarviscore/profiles/listeneragent.py +0 -292
  29. {jarviscore_framework-0.3.0.dist-info → jarviscore_framework-0.3.1.dist-info}/WHEEL +0 -0
  30. {jarviscore_framework-0.3.0.dist-info → jarviscore_framework-0.3.1.dist-info}/licenses/LICENSE +0 -0
  31. {jarviscore_framework-0.3.0.dist-info → jarviscore_framework-0.3.1.dist-info}/top_level.txt +0 -0
@@ -563,4 +563,4 @@ If significantly slower:
563
563
 
564
564
  ## Version
565
565
 
566
- Troubleshooting Guide for JarvisCore v0.2.1
566
+ Troubleshooting Guide for JarvisCore v0.3.1
@@ -146,8 +146,7 @@ mesh = Mesh(mode="distributed", config={'bind_port': 7950})
146
146
  | Profile | Best For | How It Works |
147
147
  |---------|----------|--------------|
148
148
  | **AutoAgent** | Rapid prototyping | LLM generates + executes code from prompts |
149
- | **CustomAgent** | Existing code | You provide `execute_task()` or `run()` |
150
- | **ListenerAgent** | API-first agents | Just implement `on_peer_request()` handlers |
149
+ | **CustomAgent** | Your own code | Implement `on_peer_request()` for P2P or `execute_task()` for workflows |
151
150
 
152
151
  See [AutoAgent Guide](AUTOAGENT_GUIDE.md) and [CustomAgent Guide](CUSTOMAGENT_GUIDE.md) for details.
153
152
 
@@ -227,7 +226,7 @@ asyncio.run(data_analyst_demo())
227
226
 
228
227
  ## Custom Profile Tutorial
229
228
 
230
- The **Custom Profile** (decorator/wrap approach) is deprecated. Use **CustomAgent** instead.
229
+ Use **CustomAgent** profile.
231
230
 
232
231
  See [CustomAgent Guide](CUSTOMAGENT_GUIDE.md) for:
233
232
  - Converting standalone agents to JarvisCore
@@ -546,10 +545,10 @@ Deploy agents as FastAPI services with minimal boilerplate:
546
545
 
547
546
  ```python
548
547
  from fastapi import FastAPI, Request
549
- from jarviscore.profiles import ListenerAgent
548
+ from jarviscore.profiles import CustomAgent
550
549
  from jarviscore.integrations.fastapi import JarvisLifespan
551
550
 
552
- class ProcessorAgent(ListenerAgent):
551
+ class ProcessorAgent(CustomAgent):
553
552
  role = "processor"
554
553
  capabilities = ["processing"]
555
554
 
@@ -583,9 +582,9 @@ Deploy agents to containers without a central orchestrator:
583
582
  ```python
584
583
  # In your container entrypoint
585
584
  import asyncio
586
- from jarviscore.profiles import ListenerAgent
585
+ from jarviscore.profiles import CustomAgent
587
586
 
588
- class MyAgent(ListenerAgent):
587
+ class MyAgent(CustomAgent):
589
588
  role = "worker"
590
589
  capabilities = ["processing"]
591
590
 
@@ -905,6 +904,6 @@ mesh = Mesh(config=config)
905
904
 
906
905
  ## Version
907
906
 
908
- User Guide for JarvisCore v0.3.0
907
+ User Guide for JarvisCore v0.3.1
909
908
 
910
- Last Updated: 2026-01-29
909
+ Last Updated: 2026-02-02
@@ -49,9 +49,9 @@ class JarvisLifespan:
49
49
  Example - Single Agent:
50
50
  from fastapi import FastAPI, Request
51
51
  from jarviscore.integrations.fastapi import JarvisLifespan
52
- from jarviscore.profiles import ListenerAgent
52
+ from jarviscore.profiles import CustomAgent
53
53
 
54
- class MyAgent(ListenerAgent):
54
+ class MyAgent(CustomAgent):
55
55
  role = "processor"
56
56
  capabilities = ["processing"]
57
57
 
@@ -220,9 +220,9 @@ def create_jarvis_app(
220
220
 
221
221
  Example:
222
222
  from jarviscore.integrations.fastapi import create_jarvis_app
223
- from jarviscore.profiles import ListenerAgent
223
+ from jarviscore.profiles import CustomAgent
224
224
 
225
- class MyAgent(ListenerAgent):
225
+ class MyAgent(CustomAgent):
226
226
  role = "processor"
227
227
  capabilities = ["processing"]
228
228
 
@@ -153,6 +153,7 @@ class PeerClient:
153
153
  """
154
154
  results = []
155
155
 
156
+ # Search LOCAL agents first
156
157
  if role:
157
158
  agents = self._agent_registry.get(role, [])
158
159
  for agent in agents:
@@ -166,7 +167,7 @@ class PeerClient:
166
167
  ))
167
168
 
168
169
  elif capability:
169
- # Search all agents for capability
170
+ # Search all local agents for capability
170
171
  for role_name, agents in self._agent_registry.items():
171
172
  for agent in agents:
172
173
  if agent.agent_id != self._agent_id: # Exclude self
@@ -180,7 +181,7 @@ class PeerClient:
180
181
  ))
181
182
 
182
183
  else:
183
- # Return all peers
184
+ # Return all local peers
184
185
  for role_name, agents in self._agent_registry.items():
185
186
  for agent in agents:
186
187
  if agent.agent_id != self._agent_id: # Exclude self
@@ -192,6 +193,32 @@ class PeerClient:
192
193
  status="alive"
193
194
  ))
194
195
 
196
+ # BUG FIX: Also search REMOTE agents from other nodes
197
+ # Access coordinator's _remote_agent_registry
198
+ if self._coordinator and hasattr(self._coordinator, '_remote_agent_registry'):
199
+ remote_registry = self._coordinator._remote_agent_registry
200
+
201
+ for agent_id, info in remote_registry.items():
202
+ if agent_id == self._agent_id: # Exclude self
203
+ continue
204
+
205
+ # Filter by role if specified
206
+ if role and info.get('role') != role:
207
+ continue
208
+
209
+ # Filter by capability if specified
210
+ if capability and capability not in info.get('capabilities', []):
211
+ continue
212
+
213
+ # Add remote peer
214
+ results.append(PeerInfo(
215
+ agent_id=info['agent_id'],
216
+ role=info['role'],
217
+ capabilities=info.get('capabilities', []),
218
+ node_id=info.get('node_id', 'unknown'),
219
+ status="alive"
220
+ ))
221
+
195
222
  return results
196
223
 
197
224
  @property
@@ -3,12 +3,10 @@ Execution profiles for agents.
3
3
 
4
4
  Profiles define HOW agents execute tasks:
5
5
  - AutoAgent: LLM-powered code generation + sandboxed execution
6
- - CustomAgent: User-defined logic (LangChain, MCP, raw Python)
7
- - ListenerAgent: API-first agents with background P2P listening
6
+ - CustomAgent: User-defined logic with P2P message handling
8
7
  """
9
8
 
10
9
  from .autoagent import AutoAgent
11
10
  from .customagent import CustomAgent
12
- from .listeneragent import ListenerAgent
13
11
 
14
- __all__ = ["AutoAgent", "CustomAgent", "ListenerAgent"]
12
+ __all__ = ["AutoAgent", "CustomAgent"]
@@ -1,87 +1,130 @@
1
1
  """
2
- CustomAgent - User-controlled execution profile.
3
-
4
- User provides their own implementation using any framework:
5
- - LangChain
6
- - MCP (Model Context Protocol)
7
- - CrewAI
8
- - Raw Python
9
- - Any other tool/framework
2
+ CustomAgent - User-controlled execution profile with P2P message handling.
3
+
4
+ Unified profile for building agents that:
5
+ - Handle P2P mesh communication (requests, notifications)
6
+ - Execute workflow tasks
7
+ - Integrate with HTTP APIs (FastAPI, Flask, etc.)
8
+
9
+ Example - Basic P2P Agent:
10
+ class AnalystAgent(CustomAgent):
11
+ role = "analyst"
12
+ capabilities = ["analysis"]
13
+
14
+ async def on_peer_request(self, msg):
15
+ result = await self.analyze(msg.data)
16
+ return {"status": "success", "result": result}
17
+
18
+ Example - With FastAPI:
19
+ from fastapi import FastAPI
20
+ from jarviscore.integrations.fastapi import JarvisLifespan
21
+
22
+ class ProcessorAgent(CustomAgent):
23
+ role = "processor"
24
+ capabilities = ["processing"]
25
+
26
+ async def on_peer_request(self, msg):
27
+ return {"result": await self.process(msg.data)}
28
+
29
+ app = FastAPI(lifespan=JarvisLifespan(ProcessorAgent(), mode="p2p"))
10
30
  """
11
- from typing import Dict, Any
31
+ from typing import Dict, Any, Optional
32
+ import asyncio
33
+ import logging
34
+
12
35
  from jarviscore.core.profile import Profile
13
36
 
37
+ logger = logging.getLogger(__name__)
38
+
14
39
 
15
40
  class CustomAgent(Profile):
16
41
  """
17
- Custom execution profile with full user control.
42
+ User-controlled agent profile with P2P message handling.
43
+
44
+ For P2P messaging, implement these handlers:
45
+ on_peer_request(msg) - Handle requests, return response
46
+ on_peer_notify(msg) - Handle notifications (fire-and-forget)
47
+ on_error(error, msg) - Handle errors
18
48
 
19
- User defines:
20
- - role: str
21
- - capabilities: List[str]
22
- - setup(): Initialize custom framework/tools
23
- - execute_task(): Custom execution logic
49
+ For workflow execution:
50
+ execute_task(task) - Handle workflow tasks directly
51
+ (defaults to delegating to on_peer_request)
24
52
 
25
- Framework provides:
26
- - Orchestration (task claiming, dependencies, nudging)
27
- - P2P coordination (agent discovery, task routing)
28
- - State management (crash recovery, HITL)
29
- - Cost tracking (if user provides token counts)
53
+ Configuration:
54
+ listen_timeout: Seconds to wait for messages (default: 1.0)
55
+ auto_respond: Auto-send on_peer_request return value (default: True)
30
56
 
31
- Example with LangChain:
32
- class APIAgent(CustomAgent):
33
- role = "api_client"
34
- capabilities = ["api_calls"]
57
+ Example - P2P Agent:
58
+ class AnalystAgent(CustomAgent):
59
+ role = "analyst"
60
+ capabilities = ["analysis"]
61
+
62
+ async def on_peer_request(self, msg):
63
+ result = await self.analyze(msg.data)
64
+ return {"status": "success", "result": result}
65
+
66
+ Example - With LangChain:
67
+ class LangChainAgent(CustomAgent):
68
+ role = "assistant"
69
+ capabilities = ["chat"]
35
70
 
36
71
  async def setup(self):
37
72
  await super().setup()
38
73
  from langchain.agents import Agent
39
74
  self.lc_agent = Agent(...)
40
75
 
41
- async def execute_task(self, task):
42
- result = await self.lc_agent.run(task["task"])
76
+ async def on_peer_request(self, msg):
77
+ result = await self.lc_agent.run(msg.data["query"])
43
78
  return {"status": "success", "output": result}
44
79
 
45
- Example with MCP:
80
+ Example - With MCP:
46
81
  class MCPAgent(CustomAgent):
47
82
  role = "tool_user"
48
83
  capabilities = ["mcp_tools"]
49
- mcp_server_url = "stdio://./server.py"
50
84
 
51
85
  async def setup(self):
52
86
  await super().setup()
53
87
  from mcp import Client
54
- self.mcp = Client(self.mcp_server_url)
88
+ self.mcp = Client("stdio://./server.py")
55
89
  await self.mcp.connect()
56
90
 
57
- async def execute_task(self, task):
58
- result = await self.mcp.call_tool("my_tool", task["params"])
91
+ async def on_peer_request(self, msg):
92
+ result = await self.mcp.call_tool("my_tool", msg.data)
59
93
  return {"status": "success", "data": result}
60
94
 
61
- Example with Raw Python:
62
- class DataProcessor(CustomAgent):
95
+ Example - With FastAPI:
96
+ from fastapi import FastAPI
97
+ from jarviscore.integrations.fastapi import JarvisLifespan
98
+
99
+ class ProcessorAgent(CustomAgent):
63
100
  role = "processor"
64
101
  capabilities = ["data_processing"]
65
102
 
66
- async def execute_task(self, task):
67
- # Pure Python logic
68
- data = task["params"]["data"]
69
- processed = [x * 2 for x in data]
70
- return {"status": "success", "output": processed}
103
+ async def on_peer_request(self, msg):
104
+ if msg.data.get("action") == "process":
105
+ return {"result": await self.process(msg.data["payload"])}
106
+ return {"error": "unknown action"}
107
+
108
+ agent = ProcessorAgent()
109
+ app = FastAPI(lifespan=JarvisLifespan(agent, mode="p2p"))
110
+
111
+ @app.post("/process")
112
+ async def process_endpoint(data: dict, request: Request):
113
+ # HTTP endpoint - primary interface
114
+ agent = request.app.state.jarvis_agents["processor"]
115
+ return await agent.process(data)
71
116
  """
72
117
 
73
- def __init__(self, agent_id=None):
74
- super().__init__(agent_id)
118
+ # Configuration - can be overridden in subclasses
119
+ listen_timeout: float = 1.0 # Seconds to wait for messages
120
+ auto_respond: bool = True # Automatically send response for requests
75
121
 
76
- # User can add any custom attributes
77
- # e.g., mcp_server_url, langchain_config, etc.
122
+ def __init__(self, agent_id: Optional[str] = None):
123
+ super().__init__(agent_id)
78
124
 
79
125
  async def setup(self):
80
126
  """
81
- User implements this to initialize custom framework/tools.
82
-
83
- DAY 1: Base implementation (user overrides)
84
- DAY 5+: Full examples with LangChain, MCP, etc.
127
+ Initialize agent resources. Override to add custom setup.
85
128
 
86
129
  Example:
87
130
  async def setup(self):
@@ -91,47 +134,225 @@ class CustomAgent(Profile):
91
134
  self.agent = Agent(...)
92
135
  """
93
136
  await super().setup()
94
-
95
137
  self._logger.info(f"CustomAgent setup: {self.agent_id}")
96
- self._logger.info(
97
- f" Note: Override setup() to initialize your custom framework"
138
+
139
+ # ─────────────────────────────────────────────────────────────────
140
+ # P2P Message Handling
141
+ # ─────────────────────────────────────────────────────────────────
142
+
143
+ async def run(self):
144
+ """
145
+ Listener loop - receives and dispatches P2P messages.
146
+
147
+ Runs automatically in P2P mode. Dispatches messages to:
148
+ - on_peer_request() for request-response messages
149
+ - on_peer_notify() for fire-and-forget notifications
150
+
151
+ You typically don't need to override this. Just implement the handlers.
152
+ """
153
+ self._logger.info(f"[{self.role}] Listener loop started")
154
+
155
+ while not self.shutdown_requested:
156
+ try:
157
+ # Wait for incoming message with timeout
158
+ # Timeout allows periodic shutdown_requested checks
159
+ msg = await self.peers.receive(timeout=self.listen_timeout)
160
+
161
+ if msg is None:
162
+ # Timeout - no message, continue loop to check shutdown
163
+ continue
164
+
165
+ # Dispatch to appropriate handler
166
+ await self._dispatch_message(msg)
167
+
168
+ except asyncio.CancelledError:
169
+ self._logger.debug(f"[{self.role}] Listener loop cancelled")
170
+ raise
171
+ except Exception as e:
172
+ self._logger.error(f"[{self.role}] Listener loop error: {e}")
173
+ await self.on_error(e, None)
174
+
175
+ self._logger.info(f"[{self.role}] Listener loop stopped")
176
+
177
+ async def _dispatch_message(self, msg):
178
+ """
179
+ Dispatch message to appropriate handler based on message type.
180
+
181
+ Handles:
182
+ - REQUEST messages: calls on_peer_request, sends response if auto_respond=True
183
+ - NOTIFY messages: calls on_peer_notify
184
+ """
185
+ from jarviscore.p2p.messages import MessageType
186
+
187
+ try:
188
+ # Check if this is a request (expects response)
189
+ is_request = (
190
+ msg.type == MessageType.REQUEST or
191
+ getattr(msg, 'is_request', False) or
192
+ msg.correlation_id is not None
193
+ )
194
+
195
+ if is_request:
196
+ # Request-response: call handler, optionally send response
197
+ response = await self.on_peer_request(msg)
198
+
199
+ if self.auto_respond and response is not None:
200
+ await self.peers.respond(msg, response)
201
+ self._logger.debug(
202
+ f"[{self.role}] Sent response to {msg.sender}"
203
+ )
204
+ else:
205
+ # Notification: fire-and-forget
206
+ await self.on_peer_notify(msg)
207
+
208
+ except Exception as e:
209
+ self._logger.error(
210
+ f"[{self.role}] Error handling message from {msg.sender}: {e}"
211
+ )
212
+ await self.on_error(e, msg)
213
+
214
+ # ─────────────────────────────────────────────────────────────────
215
+ # Message Handlers - Override in your agent
216
+ # ─────────────────────────────────────────────────────────────────
217
+
218
+ async def on_peer_request(self, msg) -> Any:
219
+ """
220
+ Handle incoming peer request.
221
+
222
+ Override to process request-response messages from other agents.
223
+ The return value is automatically sent as response (if auto_respond=True).
224
+
225
+ Args:
226
+ msg: IncomingMessage with:
227
+ - msg.sender: Sender agent ID or role
228
+ - msg.data: Request payload (dict)
229
+ - msg.correlation_id: For response matching (handled automatically)
230
+
231
+ Returns:
232
+ Response data (dict) to send back to the requester.
233
+ Return None to skip sending a response.
234
+
235
+ Example:
236
+ async def on_peer_request(self, msg):
237
+ action = msg.data.get("action")
238
+
239
+ if action == "analyze":
240
+ result = await self.analyze(msg.data["payload"])
241
+ return {"status": "success", "result": result}
242
+
243
+ elif action == "status":
244
+ return {"status": "ok", "queue_size": self.queue_size}
245
+
246
+ return {"status": "error", "message": f"Unknown action: {action}"}
247
+ """
248
+ return None
249
+
250
+ async def on_peer_notify(self, msg) -> None:
251
+ """
252
+ Handle incoming peer notification.
253
+
254
+ Override to process fire-and-forget messages from other agents.
255
+ No response is expected or sent.
256
+
257
+ Args:
258
+ msg: IncomingMessage with:
259
+ - msg.sender: Sender agent ID or role
260
+ - msg.data: Notification payload (dict)
261
+
262
+ Example:
263
+ async def on_peer_notify(self, msg):
264
+ event = msg.data.get("event")
265
+
266
+ if event == "task_complete":
267
+ await self.update_dashboard(msg.data)
268
+ self._logger.info(f"Task completed by {msg.sender}")
269
+
270
+ elif event == "peer_joined":
271
+ self._logger.info(f"New peer in mesh: {msg.data.get('role')}")
272
+ """
273
+ self._logger.debug(
274
+ f"[{self.role}] Received notify from {msg.sender}: "
275
+ f"{list(msg.data.keys()) if isinstance(msg.data, dict) else 'data'}"
98
276
  )
99
277
 
278
+ async def on_error(self, error: Exception, msg=None) -> None:
279
+ """
280
+ Handle errors during message processing.
281
+
282
+ Override to customize error handling (logging, alerting, metrics, etc.)
283
+ Default implementation logs the error and continues processing.
284
+
285
+ Args:
286
+ error: The exception that occurred
287
+ msg: The message being processed when error occurred (may be None)
288
+
289
+ Example:
290
+ async def on_error(self, error, msg):
291
+ # Log with context
292
+ self._logger.error(
293
+ f"Error processing message: {error}",
294
+ extra={"sender": msg.sender if msg else None}
295
+ )
296
+
297
+ # Send to error tracking service
298
+ await self.error_tracker.capture(error, context={"msg": msg})
299
+
300
+ # Optionally notify the sender of failure
301
+ if msg and msg.correlation_id:
302
+ await self.peers.respond(msg, {
303
+ "status": "error",
304
+ "error": str(error)
305
+ })
306
+ """
307
+ if msg:
308
+ self._logger.error(
309
+ f"[{self.role}] Error processing message from {msg.sender}: {error}"
310
+ )
311
+ else:
312
+ self._logger.error(f"[{self.role}] Error in listener loop: {error}")
313
+
314
+ # ─────────────────────────────────────────────────────────────────
315
+ # Workflow Compatibility
316
+ # ─────────────────────────────────────────────────────────────────
317
+
100
318
  async def execute_task(self, task: Dict[str, Any]) -> Dict[str, Any]:
101
319
  """
102
- User implements this with custom execution logic.
320
+ Execute a task (for workflow/distributed modes).
103
321
 
104
- DAY 1: Raises NotImplementedError (user must override)
105
- DAY 5+: Full examples provided
322
+ Default: Delegates to on_peer_request via synthetic message.
323
+ Override for custom workflow logic.
106
324
 
107
325
  Args:
108
- task: Task specification
326
+ task: Task specification dict
109
327
 
110
328
  Returns:
111
- Result dictionary with at least:
112
- - status: "success" or "failure"
113
- - output: Task result
114
- - error (optional): Error message if failed
115
- - tokens_used (optional): For cost tracking
116
- - cost_usd (optional): For cost tracking
329
+ Result dict with status and output
117
330
 
118
331
  Raises:
119
- NotImplementedError: User must override this method
120
-
121
- Example:
122
- async def execute_task(self, task):
123
- result = await self.my_framework.run(task)
124
- return {
125
- "status": "success",
126
- "output": result,
127
- "tokens_used": 1000, # Optional
128
- "cost_usd": 0.002 # Optional
129
- }
332
+ NotImplementedError: If on_peer_request returns None and
333
+ execute_task is not overridden
130
334
  """
335
+ from jarviscore.p2p.messages import IncomingMessage, MessageType
336
+
337
+ # Create a synthetic message to pass to the handler
338
+ synthetic_msg = IncomingMessage(
339
+ sender="workflow",
340
+ sender_node="local",
341
+ type=MessageType.REQUEST,
342
+ data=task,
343
+ correlation_id=None,
344
+ timestamp=0
345
+ )
346
+
347
+ result = await self.on_peer_request(synthetic_msg)
348
+
349
+ if result is not None:
350
+ return {"status": "success", "output": result}
351
+
131
352
  raise NotImplementedError(
132
- f"{self.__class__.__name__} must implement execute_task()\n\n"
353
+ f"{self.__class__.__name__} must implement on_peer_request() or execute_task()\n\n"
133
354
  f"Example:\n"
134
- f" async def execute_task(self, task):\n"
135
- f" result = await self.my_framework.run(task['task'])\n"
136
- f" return {{'status': 'success', 'output': result}}\n"
355
+ f" async def on_peer_request(self, msg):\n"
356
+ f" result = await self.process(msg.data)\n"
357
+ f" return {{'status': 'success', 'result': result}}\n"
137
358
  )