devduck 0.5.0__py3-none-any.whl → 0.5.3__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.

Potentially problematic release.


This version of devduck might be problematic. Click here for more details.

devduck/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.5.0'
32
- __version_tuple__ = version_tuple = (0, 5, 0)
31
+ __version__ = version = '0.5.3'
32
+ __version_tuple__ = version_tuple = (0, 5, 3)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ """DevDuck AgentCore Handler"""
3
+ import json
4
+ import os
5
+ import threading
6
+ from bedrock_agentcore.runtime import BedrockAgentCoreApp
7
+
8
+ # Configure for AgentCore deployment
9
+ os.environ["DEVDUCK_AUTO_START_SERVERS"] = "false"
10
+ os.environ["MODEL_PROVIDER"] = "bedrock"
11
+
12
+ from devduck import devduck
13
+
14
+ app = BedrockAgentCoreApp()
15
+
16
+
17
+ @app.entrypoint
18
+ async def invoke(payload, context):
19
+ """AgentCore entrypoint - streaming by default with async generator"""
20
+ mode = payload.get("mode", "streaming") # streaming (default), sync, async
21
+
22
+ query = payload.get("prompt", payload.get("text", ""))
23
+ if not query:
24
+ yield {"error": "No query provided"}
25
+ return
26
+
27
+ print(f"Mode: {mode}, Query: {query}")
28
+
29
+ agent = devduck.agent
30
+
31
+ if mode == "sync":
32
+ # Sync mode - return result directly (blocking)
33
+ try:
34
+ result = agent(query)
35
+ yield {"statusCode": 200, "response": str(result)}
36
+ except Exception as e:
37
+ print(f"Error in sync: {str(e)}")
38
+ yield {"statusCode": 500, "error": str(e)}
39
+
40
+ elif mode == "async":
41
+ # Async mode - fire and forget in background thread
42
+ task_id = app.add_async_task("devduck_processing", payload)
43
+ thread = threading.Thread(
44
+ target=lambda: _run_in_thread(agent, query, task_id), daemon=True
45
+ )
46
+ thread.start()
47
+ yield {"statusCode": 200, "task_id": task_id}
48
+
49
+ else:
50
+ # Streaming mode (default) - stream events as they happen
51
+ try:
52
+ stream = agent.stream_async(query)
53
+ async for event in stream:
54
+ print(event)
55
+ yield event
56
+ except Exception as e:
57
+ print(f"Error in streaming: {str(e)}")
58
+ yield {"error": str(e)}
59
+
60
+
61
+ def _run_in_thread(agent, query, task_id):
62
+ """Run agent in background thread for async mode"""
63
+ try:
64
+ result = agent(query)
65
+ print(f"DevDuck result: {result}")
66
+ app.complete_async_task(task_id)
67
+ except Exception as e:
68
+ print(f"Error in async thread: {str(e)}")
69
+ try:
70
+ app.complete_async_task(task_id)
71
+ except:
72
+ pass
73
+
74
+
75
+ if __name__ == "__main__":
76
+ app.run()
devduck/tools/__init__.py CHANGED
@@ -5,5 +5,48 @@ from .mcp_server import mcp_server
5
5
  from .install_tools import install_tools
6
6
  from .tray import tray
7
7
  from .ambient import ambient
8
+ from .websocket import websocket
9
+ from .ipc import ipc
10
+ from .use_github import use_github
11
+ from .create_subagent import create_subagent
12
+ from .store_in_kb import store_in_kb
13
+ from .system_prompt import system_prompt
8
14
 
9
- __all__ = ["tcp", "mcp_server", "install_tools", "tray", "ambient"]
15
+ # AgentCore tools (conditionally available)
16
+ try:
17
+ from .agentcore_config import agentcore_config
18
+ from .agentcore_invoke import agentcore_invoke
19
+ from .agentcore_logs import agentcore_logs
20
+ from .agentcore_agents import agentcore_agents
21
+
22
+ __all__ = [
23
+ "tcp",
24
+ "websocket",
25
+ "ipc",
26
+ "mcp_server",
27
+ "install_tools",
28
+ "use_github",
29
+ "create_subagent",
30
+ "store_in_kb",
31
+ "system_prompt",
32
+ "tray",
33
+ "ambient",
34
+ "agentcore_config",
35
+ "agentcore_invoke",
36
+ "agentcore_logs",
37
+ "agentcore_agents",
38
+ ]
39
+ except ImportError:
40
+ __all__ = [
41
+ "tcp",
42
+ "websocket",
43
+ "ipc",
44
+ "mcp_server",
45
+ "install_tools",
46
+ "use_github",
47
+ "create_subagent",
48
+ "store_in_kb",
49
+ "system_prompt",
50
+ "tray",
51
+ "ambient",
52
+ ]
@@ -83,6 +83,14 @@ class DevDuckTray(rumps.App):
83
83
  """Build menu with server controls and agent capabilities"""
84
84
  self.menu.clear()
85
85
 
86
+ # Test Button - First item for easy testing
87
+ self.menu.add(
88
+ rumps.MenuItem(
89
+ "🧪 Test Agent", callback=self._create_callback("what time is it?")
90
+ )
91
+ )
92
+ self.menu.add(rumps.separator)
93
+
86
94
  # Active Streams Section
87
95
  if self.active_streams:
88
96
  self.menu.add(rumps.MenuItem("🌊 Active Streams", callback=None))
@@ -0,0 +1,197 @@
1
+ """AgentCore Agents Tool - List and manage deployed DevDuck agents."""
2
+
3
+ import json
4
+ from typing import Any, Dict, Optional
5
+ from strands import tool
6
+
7
+
8
+ @tool
9
+ def agentcore_agents(
10
+ action: str = "list",
11
+ agent_id: Optional[str] = None,
12
+ agent_name: Optional[str] = None,
13
+ max_results: int = 100,
14
+ region: str = "us-west-2",
15
+ ) -> Dict[str, Any]:
16
+ """Manage and discover Bedrock AgentCore agent runtimes.
17
+
18
+ Args:
19
+ action: Agent operation (list, get, find_by_name)
20
+ agent_id: Agent runtime ID (for "get" action)
21
+ agent_name: Agent name to search (for "find_by_name")
22
+ max_results: Max results for list (default: 100)
23
+ region: AWS region (default: us-west-2)
24
+
25
+ Returns:
26
+ Dict with status and agent information
27
+
28
+ Examples:
29
+ # List all agents
30
+ agentcore_agents(action="list")
31
+
32
+ # Get specific agent
33
+ agentcore_agents(action="get", agent_id="devduck-UrvQvkH6H7")
34
+
35
+ # Find by name
36
+ agentcore_agents(action="find_by_name", agent_name="devduck")
37
+ """
38
+ try:
39
+ import boto3
40
+ from botocore.exceptions import ClientError
41
+
42
+ client = boto3.client("bedrock-agentcore-control", region_name=region)
43
+
44
+ if action == "list":
45
+ all_agents = []
46
+ next_token = None
47
+
48
+ while True:
49
+ params = {"maxResults": min(max_results - len(all_agents), 100)}
50
+ if next_token:
51
+ params["nextToken"] = next_token
52
+
53
+ response = client.list_agent_runtimes(**params)
54
+ agents = response.get("agentRuntimes", [])
55
+ all_agents.extend(agents)
56
+
57
+ if len(all_agents) >= max_results:
58
+ all_agents = all_agents[:max_results]
59
+ break
60
+
61
+ next_token = response.get("nextToken")
62
+ if not next_token:
63
+ break
64
+
65
+ # Format output
66
+ agent_list = []
67
+ for agent in all_agents:
68
+ agent_list.append(
69
+ {
70
+ "name": agent.get("agentRuntimeName"),
71
+ "id": agent.get("agentRuntimeId"),
72
+ "arn": agent.get("agentRuntimeArn"),
73
+ "created": str(agent.get("createdAt")),
74
+ "updated": str(agent.get("lastUpdatedAt")),
75
+ }
76
+ )
77
+
78
+ return {
79
+ "status": "success",
80
+ "content": [
81
+ {"text": f"**Found {len(agent_list)} agents:**\n"},
82
+ {"text": json.dumps(agent_list, indent=2)},
83
+ ],
84
+ }
85
+
86
+ elif action == "get":
87
+ if not agent_id:
88
+ return {
89
+ "status": "error",
90
+ "content": [{"text": "agent_id required for get action"}],
91
+ }
92
+
93
+ response = client.get_agent_runtime(agentRuntimeId=agent_id)
94
+
95
+ agent_info = {
96
+ "id": response.get("agentRuntimeId"),
97
+ "arn": response.get("agentRuntimeArn"),
98
+ "name": response.get("agentRuntimeName"),
99
+ "status": response.get("status"),
100
+ "roleArn": response.get("roleArn"),
101
+ "created": str(response.get("createdAt")),
102
+ "updated": str(response.get("lastUpdatedAt")),
103
+ }
104
+
105
+ # Add container info
106
+ if "agentRuntimeArtifact" in response:
107
+ container = response["agentRuntimeArtifact"].get(
108
+ "containerConfiguration", {}
109
+ )
110
+ if container:
111
+ agent_info["containerUri"] = container.get("containerUri")
112
+
113
+ # Add network info
114
+ if "networkConfiguration" in response:
115
+ agent_info["networkMode"] = response["networkConfiguration"].get(
116
+ "networkMode"
117
+ )
118
+
119
+ return {
120
+ "status": "success",
121
+ "content": [
122
+ {"text": "**Agent Details:**\n"},
123
+ {"text": json.dumps(agent_info, indent=2)},
124
+ ],
125
+ }
126
+
127
+ elif action == "find_by_name":
128
+ if not agent_name:
129
+ return {
130
+ "status": "error",
131
+ "content": [
132
+ {"text": "agent_name required for find_by_name action"}
133
+ ],
134
+ }
135
+
136
+ # List and search
137
+ all_agents = []
138
+ next_token = None
139
+
140
+ while True:
141
+ params = {"maxResults": 100}
142
+ if next_token:
143
+ params["nextToken"] = next_token
144
+
145
+ response = client.list_agent_runtimes(**params)
146
+ agents = response.get("agentRuntimes", [])
147
+ all_agents.extend(agents)
148
+
149
+ next_token = response.get("nextToken")
150
+ if not next_token:
151
+ break
152
+
153
+ # Find match
154
+ matching = None
155
+ for agent in all_agents:
156
+ if agent.get("agentRuntimeName") == agent_name:
157
+ matching = agent
158
+ break
159
+
160
+ if matching:
161
+ return {
162
+ "status": "success",
163
+ "content": [
164
+ {"text": f"✅ **Found: {agent_name}**\n"},
165
+ {"text": json.dumps(matching, indent=2, default=str)},
166
+ ],
167
+ }
168
+ else:
169
+ return {
170
+ "status": "error",
171
+ "content": [{"text": f"Agent not found: {agent_name}"}],
172
+ }
173
+
174
+ else:
175
+ return {
176
+ "status": "error",
177
+ "content": [
178
+ {
179
+ "text": f"Unknown action: {action}. Valid: list, get, find_by_name"
180
+ }
181
+ ],
182
+ }
183
+
184
+ except ClientError as e:
185
+ error_code = e.response.get("Error", {}).get("Code", "Unknown")
186
+ error_message = e.response.get("Error", {}).get("Message", str(e))
187
+
188
+ return {
189
+ "status": "error",
190
+ "content": [
191
+ {"text": f"**AWS Error ({error_code}):** {error_message}"},
192
+ {"text": f"**Region:** {region}"},
193
+ ],
194
+ }
195
+
196
+ except Exception as e:
197
+ return {"status": "error", "content": [{"text": f"Error: {str(e)}"}]}