devduck 0.1.1766644714__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.

Potentially problematic release.


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

Files changed (36) hide show
  1. devduck/__init__.py +591 -1092
  2. devduck/_version.py +2 -2
  3. devduck/install.sh +42 -0
  4. devduck/test_redduck.py +1 -0
  5. devduck/tools/__init__.py +4 -44
  6. devduck/tools/install_tools.py +2 -103
  7. devduck/tools/mcp_server.py +6 -34
  8. devduck/tools/tcp.py +7 -6
  9. devduck/tools/websocket.py +2 -8
  10. devduck-0.2.0.dist-info/METADATA +143 -0
  11. devduck-0.2.0.dist-info/RECORD +16 -0
  12. {devduck-0.1.1766644714.dist-info → devduck-0.2.0.dist-info}/entry_points.txt +0 -1
  13. devduck-0.2.0.dist-info/licenses/LICENSE +21 -0
  14. devduck/agentcore_handler.py +0 -76
  15. devduck/tools/_ambient_input.py +0 -423
  16. devduck/tools/_tray_app.py +0 -530
  17. devduck/tools/agentcore_agents.py +0 -197
  18. devduck/tools/agentcore_config.py +0 -441
  19. devduck/tools/agentcore_invoke.py +0 -423
  20. devduck/tools/agentcore_logs.py +0 -320
  21. devduck/tools/ambient.py +0 -157
  22. devduck/tools/create_subagent.py +0 -659
  23. devduck/tools/fetch_github_tool.py +0 -201
  24. devduck/tools/ipc.py +0 -546
  25. devduck/tools/scraper.py +0 -935
  26. devduck/tools/speech_to_speech.py +0 -850
  27. devduck/tools/state_manager.py +0 -292
  28. devduck/tools/store_in_kb.py +0 -187
  29. devduck/tools/system_prompt.py +0 -608
  30. devduck/tools/tray.py +0 -247
  31. devduck/tools/use_github.py +0 -438
  32. devduck-0.1.1766644714.dist-info/METADATA +0 -717
  33. devduck-0.1.1766644714.dist-info/RECORD +0 -33
  34. devduck-0.1.1766644714.dist-info/licenses/LICENSE +0 -201
  35. {devduck-0.1.1766644714.dist-info → devduck-0.2.0.dist-info}/WHEEL +0 -0
  36. {devduck-0.1.1766644714.dist-info → devduck-0.2.0.dist-info}/top_level.txt +0 -0
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.1.1766644714'
32
- __version_tuple__ = version_tuple = (0, 1, 1766644714)
31
+ __version__ = version = '0.2.0'
32
+ __version_tuple__ = version_tuple = (0, 2, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
devduck/install.sh ADDED
@@ -0,0 +1,42 @@
1
+ #!/bin/bash
2
+ # 🦆 DevDuck installer - Extreme minimalist agent
3
+
4
+ echo "🦆 Installing Devduck..."
5
+
6
+ # Check Python
7
+ if ! command -v python3 &> /dev/null; then
8
+ echo "❌ Python 3 not found. Please install Python 3.8+"
9
+ exit 1
10
+ fi
11
+
12
+ # Check Ollama
13
+ if ! command -v ollama &> /dev/null; then
14
+ echo "⚠️ Ollama not found. Installing..."
15
+ if [[ "$OSTYPE" == "darwin"* ]]; then
16
+ # macOS
17
+ if command -v brew &> /dev/null; then
18
+ brew install ollama
19
+ else
20
+ curl -fsSL https://ollama.ai/install.sh | sh
21
+ fi
22
+ else
23
+ # Linux
24
+ curl -fsSL https://ollama.ai/install.sh | sh
25
+ fi
26
+ fi
27
+
28
+ # Start ollama service
29
+ echo "🦆 Starting Ollama service..."
30
+ ollama serve &
31
+ sleep 2
32
+
33
+ # Pull a basic model
34
+ echo "🦆 Pulling basic model..."
35
+ ollama pull qwen3:1.7b
36
+
37
+ # Test devduck
38
+ echo "🦆 Testing Devduck..."
39
+ python3 __init__.py "what's 5*7?"
40
+
41
+ echo "✅ Devduck installed successfully!"
42
+ echo "Usage: python3 __init__.py 'your question'"
devduck/test_redduck.py CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env python3
1
2
  """🦆 DevDuck test suite"""
2
3
 
3
4
 
devduck/tools/__init__.py CHANGED
@@ -1,47 +1,7 @@
1
- """
2
- DevDuck Tools Package
1
+ """DevDuck tools package."""
3
2
 
4
- This module exports all available tools for devduck.
5
- """
6
-
7
- from .agentcore_agents import agentcore_agents
8
- from .agentcore_config import agentcore_config
9
- from .agentcore_invoke import agentcore_invoke
10
- from .agentcore_logs import agentcore_logs
11
- from .ambient import ambient
12
- from .create_subagent import create_subagent
13
- from .fetch_github_tool import fetch_github_tool
14
- from .install_tools import install_tools
15
- from .ipc import ipc
16
- from .mcp_server import mcp_server
17
- from .scraper import scraper
18
- from .speech_to_speech import speech_to_speech
19
- from .state_manager import state_manager
20
- from .store_in_kb import store_in_kb
21
- from .system_prompt import system_prompt
22
3
  from .tcp import tcp
23
- from .tray import tray
24
- from .use_github import use_github
25
- from .websocket import websocket
4
+ from .mcp_server import mcp_server
5
+ from .install_tools import install_tools
26
6
 
27
- __all__ = [
28
- "agentcore_agents",
29
- "agentcore_config",
30
- "agentcore_invoke",
31
- "agentcore_logs",
32
- "ambient",
33
- "create_subagent",
34
- "fetch_github_tool",
35
- "install_tools",
36
- "ipc",
37
- "mcp_server",
38
- "scraper",
39
- "speech_to_speech",
40
- "state_manager",
41
- "store_in_kb",
42
- "system_prompt",
43
- "tcp",
44
- "tray",
45
- "use_github",
46
- "websocket",
47
- ]
7
+ __all__ = ["tcp", "mcp_server", "install_tools"]
@@ -29,7 +29,7 @@ def install_tools(
29
29
  and loading their tools into the agent's registry at runtime.
30
30
 
31
31
  Args:
32
- action: Action to perform - "install", "load", "install_and_load", "list_loaded", "list_available"
32
+ action: Action to perform - "install", "load", "install_and_load", "list_loaded"
33
33
  package: Python package to install (e.g., "strands-agents-tools", "strands-fun-tools")
34
34
  module: Module to import tools from (e.g., "strands_tools", "strands_fun_tools")
35
35
  tool_names: Optional list of specific tools to load. If None, loads all available tools
@@ -39,13 +39,6 @@ def install_tools(
39
39
  Result dictionary with status and content
40
40
 
41
41
  Examples:
42
- # List available tools in a package (without loading)
43
- install_tools(
44
- action="list_available",
45
- package="strands-fun-tools",
46
- module="strands_fun_tools"
47
- )
48
-
49
42
  # Install and load all tools from strands-agents-tools
50
43
  install_tools(
51
44
  action="install_and_load",
@@ -86,15 +79,13 @@ def install_tools(
86
79
  return _load_tools_from_module(module, tool_names, agent)
87
80
  elif action == "list_loaded":
88
81
  return _list_loaded_tools(agent)
89
- elif action == "list_available":
90
- return _list_available_tools(package, module)
91
82
  else:
92
83
  return {
93
84
  "status": "error",
94
85
  "content": [
95
86
  {
96
87
  "text": f"❌ Unknown action: {action}\n\n"
97
- f"Valid actions: install, load, install_and_load, list_loaded, list_available"
88
+ f"Valid actions: install, load, install_and_load, list_loaded"
98
89
  }
99
90
  ],
100
91
  }
@@ -315,95 +306,3 @@ def _list_loaded_tools(agent: Any) -> Dict[str, Any]:
315
306
  "status": "error",
316
307
  "content": [{"text": f"❌ Failed to list tools: {str(e)}"}],
317
308
  }
318
-
319
-
320
- def _list_available_tools(package: Optional[str], module: str) -> Dict[str, Any]:
321
- """List available tools in a package without loading them."""
322
- if not module:
323
- return {
324
- "status": "error",
325
- "content": [
326
- {"text": "❌ module parameter is required for list_available action"}
327
- ],
328
- }
329
-
330
- try:
331
- # Try to import the module
332
- try:
333
- imported_module = importlib.import_module(module)
334
- logger.info(f"Module {module} already installed")
335
- except ImportError:
336
- # Module not installed - try to install package first
337
- if not package:
338
- return {
339
- "status": "error",
340
- "content": [
341
- {
342
- "text": f"❌ Module {module} not found and no package specified to install.\n\n"
343
- f"Please provide the 'package' parameter to install first."
344
- }
345
- ],
346
- }
347
-
348
- logger.info(f"Module {module} not found, installing package {package}")
349
- install_result = _install_package(package)
350
- if install_result["status"] == "error":
351
- return install_result
352
-
353
- # Try importing again after installation
354
- try:
355
- imported_module = importlib.import_module(module)
356
- except ImportError as e:
357
- return {
358
- "status": "error",
359
- "content": [
360
- {
361
- "text": f"❌ Failed to import {module} even after installing {package}: {str(e)}"
362
- }
363
- ],
364
- }
365
-
366
- # Discover tools in the module
367
- available_tools = {}
368
- for attr_name in dir(imported_module):
369
- attr = getattr(imported_module, attr_name)
370
- # Check if it's a tool (has tool_name and tool_spec attributes)
371
- if hasattr(attr, "tool_name") and hasattr(attr, "tool_spec"):
372
- tool_spec = attr.tool_spec
373
- description = tool_spec.get("description", "No description available")
374
- available_tools[attr.tool_name] = description
375
-
376
- if not available_tools:
377
- return {
378
- "status": "success",
379
- "content": [{"text": f"⚠️ No tools found in module: {module}"}],
380
- }
381
-
382
- # Build result message
383
- result_lines = [
384
- f"📦 **Available Tools in {module} ({len(available_tools)})**\n"
385
- ]
386
-
387
- for tool_name, description in sorted(available_tools.items()):
388
- # Truncate long descriptions
389
- if len(description) > 100:
390
- description = description[:97] + "..."
391
-
392
- result_lines.append(f"**{tool_name}**")
393
- result_lines.append(f" {description}\n")
394
-
395
- result_lines.append(f"\n💡 To load these tools, use:")
396
- result_lines.append(f" install_tools(action='load', module='{module}')")
397
- result_lines.append(f" # Or load specific tools:")
398
- result_lines.append(
399
- f" install_tools(action='load', module='{module}', tool_names=['tool1', 'tool2'])"
400
- )
401
-
402
- return {"status": "success", "content": [{"text": "\n".join(result_lines)}]}
403
-
404
- except Exception as e:
405
- logger.exception(f"Error listing available tools from {module}")
406
- return {
407
- "status": "error",
408
- "content": [{"text": f"❌ Failed to list available tools: {str(e)}"}],
409
- }
@@ -190,11 +190,9 @@ def _start_mcp_server(
190
190
  agent_invoke_tool = types.Tool(
191
191
  name="devduck",
192
192
  description=(
193
- "Invoke a FULL DevDuck instance with complete capabilities. "
194
- "Each invocation creates a fresh DevDuck agent with self-healing, "
195
- "hot-reload, all tools, knowledge base integration, and system prompt building. "
196
- "Use this for complex queries requiring reasoning, multi-tool orchestration, "
197
- "or when you need the complete DevDuck experience via MCP."
193
+ "Invoke the full DevDuck agent with a natural language prompt. "
194
+ "Use this for complex queries that require reasoning across multiple tools "
195
+ "or when you need a conversational response from the agent."
198
196
  ),
199
197
  inputSchema={
200
198
  "type": "object",
@@ -230,7 +228,7 @@ def _start_mcp_server(
230
228
  try:
231
229
  logger.debug(f"call_tool: name={name}, arguments={arguments}")
232
230
 
233
- # Handle agent invocation tool - create a full DevDuck instance
231
+ # Handle agent invocation tool - use the devduck instance directly
234
232
  if name == "devduck" and expose_agent:
235
233
  prompt = arguments.get("prompt")
236
234
  if not prompt:
@@ -243,34 +241,8 @@ def _start_mcp_server(
243
241
 
244
242
  logger.debug(f"Invoking devduck with prompt: {prompt[:100]}...")
245
243
 
246
- # Create a NEW DevDuck instance for this MCP invocation
247
- # This gives full DevDuck power: self-healing, hot-reload, all tools, etc.
248
- try:
249
- from devduck import DevDuck
250
-
251
- # Create fresh DevDuck instance (no auto-start to avoid recursion)
252
- mcp_devduck = DevDuck(auto_start_servers=False)
253
- mcp_agent = mcp_devduck.agent
254
-
255
- if not mcp_agent:
256
- return [
257
- types.TextContent(
258
- type="text",
259
- text="❌ Error: Failed to create DevDuck instance",
260
- )
261
- ]
262
-
263
- # Execute with full DevDuck capabilities
264
- result = mcp_agent(prompt)
265
-
266
- except Exception as e:
267
- logger.error(f"DevDuck creation failed: {e}", exc_info=True)
268
- return [
269
- types.TextContent(
270
- type="text",
271
- text=f"❌ Error creating DevDuck instance: {str(e)}",
272
- )
273
- ]
244
+ # Use the devduck agent directly (don't create a new instance)
245
+ result = agent(prompt)
274
246
 
275
247
  # Extract text response from agent result
276
248
  response_text = str(result)
devduck/tools/tcp.py CHANGED
@@ -216,6 +216,13 @@ def handle_client(
216
216
  )
217
217
 
218
218
  try:
219
+ # Send welcome message
220
+ welcome_msg = "🦆 Welcome to DevDuck TCP Server!\n"
221
+ welcome_msg += (
222
+ "Real-time streaming enabled - responses stream as they're generated.\n"
223
+ )
224
+ welcome_msg += "Send a message or 'exit' to close the connection.\n\n"
225
+ streaming_handler._send(welcome_msg)
219
226
 
220
227
  while True:
221
228
  # Receive data from the client
@@ -316,12 +323,6 @@ def run_server(
316
323
  if SERVER_THREADS[port]["running"]:
317
324
  logger.error(f"Error accepting connection: {e}")
318
325
 
319
- except OSError as e:
320
- # Port conflict - handled upstream, no need for scary errors
321
- if "Address already in use" in str(e):
322
- logger.debug(f"Port {port} unavailable (handled upstream)")
323
- else:
324
- logger.error(f"Server error on {host}:{port}: {e}")
325
326
  except Exception as e:
326
327
  logger.error(f"Server error on {host}:{port}: {e}")
327
328
  finally:
@@ -261,7 +261,7 @@ async def handle_websocket_client(websocket, system_prompt: str):
261
261
  # Send welcome message
262
262
  welcome = {
263
263
  "type": "connected",
264
- "data": "🦆 Welcome to DevDuck!",
264
+ "data": "🦆 Welcome to DevDuck WebSocket Server! Real-time streaming enabled.",
265
265
  "timestamp": time.time(),
266
266
  }
267
267
  await websocket.send(json.dumps(welcome))
@@ -347,14 +347,8 @@ def run_websocket_server(
347
347
  asyncio.set_event_loop(loop)
348
348
  WS_SERVER_THREADS[port]["loop"] = loop
349
349
  loop.run_until_complete(start_server())
350
- except OSError as e:
351
- # Port conflict - handled upstream, no need for scary errors
352
- if "Address already in use" in str(e) or "address already in use" in str(e):
353
- logger.debug(f"Port {port} unavailable (handled upstream)")
354
- else:
355
- logger.error(f"WebSocket server error on {host}:{port}: {e}")
356
350
  except Exception as e:
357
- logger.error(f"WebSocket server error on {host}:{port}: {e}")
351
+ logger.error(f"WebSocket server error on {host}:{port}: {e}", exc_info=True)
358
352
  finally:
359
353
  logger.info(f"WebSocket Server on {host}:{port} stopped")
360
354
  WS_SERVER_THREADS[port]["running"] = False
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: devduck
3
+ Version: 0.2.0
4
+ Summary: 🦆 Extreme minimalist self-adapting AI agent - one file, self-healing, runtime dependencies
5
+ Author-email: duck <hey@devduck.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/cagataycali/devduck
8
+ Project-URL: Repository, https://github.com/cagataycali/devduck.git
9
+ Project-URL: Documentation, https://github.com/cagataycali/devduck#readme
10
+ Project-URL: Bug Tracker, https://github.com/cagataycali/devduck/issues
11
+ Keywords: ai,agent,minimalist,self-healing,ollama,strands-agents
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Classifier: Topic :: Utilities
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: strands-agents
30
+ Requires-Dist: strands-agents[ollama]
31
+ Requires-Dist: strands-agents[openai]
32
+ Requires-Dist: strands-agents[anthropic]
33
+ Requires-Dist: strands-agents-tools
34
+ Requires-Dist: strands-fun-tools[audio]
35
+ Requires-Dist: strands-fun-tools[vision]
36
+ Requires-Dist: strands-fun-tools[all]
37
+ Requires-Dist: websockets
38
+ Requires-Dist: prompt_toolkit
39
+ Dynamic: license-file
40
+
41
+ # 🦆 DevDuck
42
+
43
+ **One file. Self-healing. Adaptive.**
44
+
45
+ Minimalist AI agent that fixes itself when things break.
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pipx install devduck
51
+ ```
52
+
53
+ Requires: Python 3.10+, Ollama running
54
+
55
+ ## Use
56
+
57
+ ```bash
58
+ # Start DevDuck (auto-starts TCP, WebSocket, MCP servers)
59
+ devduck
60
+
61
+ # CLI mode
62
+ devduck "what's the time?"
63
+
64
+ # Python
65
+ import devduck
66
+ devduck("calculate 2+2")
67
+ ```
68
+
69
+ ## Auto-Started Servers
70
+
71
+ When you run `devduck`, three servers start automatically:
72
+
73
+ - **🌐 Web UI**: [http://cagataycali.github.io/devduck](http://cagataycali.github.io/devduck) (auto-connects)
74
+ - **🔌 TCP**: `nc localhost 9999` (raw socket)
75
+ - **🌊 WebSocket**: `ws://localhost:8080` (structured JSON)
76
+ - **🔗 MCP**: `http://localhost:8000/mcp` (Model Context Protocol)
77
+
78
+ ### Connect via MCP
79
+
80
+ Add to your MCP client (e.g., Claude Desktop):
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "devduck": {
86
+ "command": "uvx",
87
+ "args": [
88
+ "strands-mcp-server",
89
+ "--upstream-url",
90
+ "http://localhost:8000/mcp/"
91
+ ],
92
+ "disabled": false
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### Connect via Terminal
99
+
100
+ ```bash
101
+ # Direct TCP connection
102
+ nc localhost 9999
103
+ > what's the time?
104
+ ```
105
+
106
+ ## Features
107
+
108
+ - **Self-healing** - Auto-fixes deps, models, errors
109
+ - **Hot-reload** - Create tools in `./tools/*.py`, use instantly
110
+ - **Adaptive** - Picks model based on OS (macOS: 1.7b, Linux: 30b)
111
+ - **14 tools** - shell, editor, files, python, calculator, tcp, etc.
112
+ - **History aware** - Remembers shell/conversation context
113
+ - **Multi-protocol** - TCP, WebSocket, MCP, CLI, Python
114
+
115
+ ## Create Tool
116
+
117
+ ```python
118
+ # ./tools/greet.py
119
+ from strands import tool
120
+
121
+ @tool
122
+ def greet(name: str) -> str:
123
+ return f"Hello {name}!"
124
+ ```
125
+
126
+ Save. Done. Use immediately.
127
+
128
+ ## Multi-Model
129
+
130
+ ```bash
131
+ export MODEL_PROVIDER="bedrock"
132
+ export STRANDS_MODEL_ID="us.anthropic.claude-sonnet-4-5-20250929-v1:0"
133
+ export STRANDS_ADDITIONAL_REQUEST_FIELDS='{"anthropic_beta": ["interleaved-thinking-2025-05-14", "context-1m-2025-08-07"], "thinking": {"type": "enabled", "budget_tokens": 2048}}'
134
+ export STRANDS_MAX_TOKENS="64000"
135
+
136
+ devduck "analyze data"
137
+ ```
138
+
139
+ ---
140
+
141
+ **Quack.** 🦆
142
+
143
+ *Built with [Strands Agents SDK](https://github.com/strands-agents/sdk-python)*
@@ -0,0 +1,16 @@
1
+ devduck/__init__.py,sha256=w7H7wKWkeD0fKUhLMJ4dxxrWCkRH_R7w3maxjJI0WgU,51881
2
+ devduck/__main__.py,sha256=aeF2RR4k7lzSR2X1QKV9XQPCKhtsH0JYUv2etBBqmL0,145
3
+ devduck/_version.py,sha256=Dg8AmJomLVpjKL6prJylOONZAPRtB86LOce7dorQS_A,704
4
+ devduck/install.sh,sha256=tYq2WWZFCBEMbxCneKAw3GSNAG1zNhpd-kzW1l5ZISw,990
5
+ devduck/test_redduck.py,sha256=nqRchR7d54jWGx7JN5tji2ZV4Ek4L9s-P7hp0mKjA0Y,1773
6
+ devduck/tools/__init__.py,sha256=mu3V4jL2ACN4f-pnUID_A2p6o3Yc_-V_y9071PduCR0,177
7
+ devduck/tools/install_tools.py,sha256=wm_67b9IfY-2wRuWgxuEKhaSIV5vNfbGmZL3G9dGi2A,10348
8
+ devduck/tools/mcp_server.py,sha256=oyF1gb7K-OlxyJLUO3L-vNo2ajKzIrcnT1crwKMOkhU,20118
9
+ devduck/tools/tcp.py,sha256=4KtyRlgaOLKXT3PU0yFRM79FoOkn3-S10dVL4L5iG80,22063
10
+ devduck/tools/websocket.py,sha256=ryKE1WbfaTFguwp-AzJlyCOifHE5uXJAVdHD8jecJgQ,16681
11
+ devduck-0.2.0.dist-info/licenses/LICENSE,sha256=CVGEiNh6cW1mgAKW83Q0P4xrQEXvqc6W-rb789W_IHM,1060
12
+ devduck-0.2.0.dist-info/METADATA,sha256=yl_DpWxBYGIWkbehwUFH-jyD7W1O945cRCJsE9Ufzew,3902
13
+ devduck-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ devduck-0.2.0.dist-info/entry_points.txt,sha256=BAMQaIg_BLZQOTk12bT7hy1dE9oGPLt-_dTbI4cnBnQ,40
15
+ devduck-0.2.0.dist-info/top_level.txt,sha256=ySXWlVronp8xHYfQ_Hdfr463e0EnbWuqyuxs94EU7yk,8
16
+ devduck-0.2.0.dist-info/RECORD,,
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
- dev = devduck:cli
3
2
  devduck = devduck:cli
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 maxs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,76 +0,0 @@
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()