patchpal 0.25.1__tar.gz → 0.25.3__tar.gz
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.
- {patchpal-0.25.1/patchpal.egg-info → patchpal-0.25.3}/PKG-INFO +1 -1
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/__init__.py +1 -1
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/agent/function_calling.py +27 -3
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/cli/sandbox.py +21 -1
- {patchpal-0.25.1 → patchpal-0.25.3/patchpal.egg-info}/PKG-INFO +1 -1
- {patchpal-0.25.1 → patchpal-0.25.3}/LICENSE +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/MANIFEST.in +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/README.md +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/agent/__init__.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/agent/bedrock_profile_utils.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/agent/react.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/cli/__init__.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/cli/autopilot.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/cli/interactive.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/cli/mcp.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/cli/streaming.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/config.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/context.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/permissions.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/prompts/react_prompt.md +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/prompts/system_prompt.md +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/skills.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/__init__.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/audit.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/browser_tools.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/code_analysis.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/common.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/definitions.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/file_reading.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/file_writing.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/find_tool.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/grep_tool.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/image_handler.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/mcp.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/repo_map.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/shell_tools.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/todo_tools.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/tool_schema.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/user_interaction.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal/tools/web_tools.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal.egg-info/SOURCES.txt +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal.egg-info/dependency_links.txt +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal.egg-info/entry_points.txt +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal.egg-info/requires.txt +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/patchpal.egg-info/top_level.txt +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/pyproject.toml +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/setup.cfg +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_agent.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_cli.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_config_dynamic.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_context.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_custom_tools.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_enabled_tools.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_find_tool.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_guardrails.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_image_blocking.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_maximum_security.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_mcp_config.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_memory.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_memory_locations.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_operational_safety.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_optional_tools.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_permissions.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_react.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_reasoning_content.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_repo_map.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_simplified_prompt.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_skills.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_streaming.py +0 -0
- {patchpal-0.25.1 → patchpal-0.25.3}/tests/test_tools.py +0 -0
|
@@ -1161,6 +1161,12 @@ Repository root takes priority if both exist."""
|
|
|
1161
1161
|
The agent's final response
|
|
1162
1162
|
"""
|
|
1163
1163
|
# Agent loop
|
|
1164
|
+
# Track consecutive empty responses (no content + no tool calls).
|
|
1165
|
+
# Common with some local models (e.g., GPT-OSS-120B on vLLM) due to
|
|
1166
|
+
# intermittent harmony-format parsing hiccups. A bounded retry usually
|
|
1167
|
+
# recovers since the failure is probabilistic.
|
|
1168
|
+
empty_response_retries = 0
|
|
1169
|
+
empty_response_max_retries = int(os.environ.get("PATCHPAL_EMPTY_RETRY_MAX", "2"))
|
|
1164
1170
|
for iteration in range(max_iterations):
|
|
1165
1171
|
# Clear any stale pending images from previous iteration (safety check)
|
|
1166
1172
|
# Images should already be injected, but this prevents accumulation on error
|
|
@@ -1335,6 +1341,8 @@ Repository root takes priority if both exist."""
|
|
|
1335
1341
|
|
|
1336
1342
|
# Check if there are tool calls
|
|
1337
1343
|
if hasattr(assistant_message, "tool_calls") and assistant_message.tool_calls:
|
|
1344
|
+
# A non-empty turn succeeded; reset the empty-response retry budget
|
|
1345
|
+
empty_response_retries = 0
|
|
1338
1346
|
# Print explanation text before executing tools (render as markdown)
|
|
1339
1347
|
if assistant_message.content and assistant_message.content.strip():
|
|
1340
1348
|
console = Console()
|
|
@@ -1834,9 +1842,25 @@ Repository root takes priority if both exist."""
|
|
|
1834
1842
|
if assistant_message.content:
|
|
1835
1843
|
return assistant_message.content
|
|
1836
1844
|
else:
|
|
1837
|
-
# Model returned no content and no tool calls
|
|
1838
|
-
# This
|
|
1839
|
-
#
|
|
1845
|
+
# Model returned no content and no tool calls.
|
|
1846
|
+
# This happens intermittently with some local models
|
|
1847
|
+
# (e.g., GPT-OSS-120B on vLLM) due to harmony-format parsing
|
|
1848
|
+
# hiccups. Because the failure is probabilistic, retry the turn
|
|
1849
|
+
# a bounded number of times before giving up.
|
|
1850
|
+
if empty_response_retries < empty_response_max_retries:
|
|
1851
|
+
empty_response_retries += 1
|
|
1852
|
+
# Drop the empty assistant message so it doesn't pollute history
|
|
1853
|
+
if self.messages and self.messages[-1] is assistant_msg:
|
|
1854
|
+
self.messages.pop()
|
|
1855
|
+
print(
|
|
1856
|
+
f"\033[2m↻ Empty response from model; retrying "
|
|
1857
|
+
f"({empty_response_retries}/{empty_response_max_retries})...\033[0m",
|
|
1858
|
+
flush=True,
|
|
1859
|
+
)
|
|
1860
|
+
continue
|
|
1861
|
+
|
|
1862
|
+
# Retries exhausted: surface the honest failure.
|
|
1863
|
+
# Using "NO_RESPONSE:" prefix for programmatic detection.
|
|
1840
1864
|
return "NO_RESPONSE: Model returned empty response. Try rephrasing your request or type 'continue' to prompt again."
|
|
1841
1865
|
|
|
1842
1866
|
# Max iterations reached
|
|
@@ -198,7 +198,14 @@ def detect_llm_endpoints_from_env():
|
|
|
198
198
|
or os.environ.get("AWS_REGION_NAME") # LiteLLM-specific
|
|
199
199
|
)
|
|
200
200
|
if aws_region and (
|
|
201
|
-
os.environ.get("AWS_ACCESS_KEY_ID")
|
|
201
|
+
os.environ.get("AWS_ACCESS_KEY_ID")
|
|
202
|
+
or os.environ.get("AWS_SECRET_ACCESS_KEY")
|
|
203
|
+
# SSO/ADFS profiles (created via `aws configure sso`) authenticate via a
|
|
204
|
+
# named profile and a cached SSO token instead of static access keys.
|
|
205
|
+
# In that case AWS_PROFILE is set but AWS_ACCESS_KEY_ID is not, so detect
|
|
206
|
+
# it here too — otherwise Bedrock endpoints would be omitted from the
|
|
207
|
+
# firewall allowlist and traffic would be blocked.
|
|
208
|
+
or os.environ.get("AWS_PROFILE")
|
|
202
209
|
):
|
|
203
210
|
# Add Bedrock endpoints for the region
|
|
204
211
|
# Support both GovCloud and standard regions
|
|
@@ -659,6 +666,19 @@ def build_container_args(sandbox_args, patchpal_args, env_file_keys=None):
|
|
|
659
666
|
container_args.extend(["-v", f"{patchpal_dir}:/root/.patchpal"])
|
|
660
667
|
mounted_paths.add(patchpal_dir)
|
|
661
668
|
|
|
669
|
+
# Mount ~/.aws if it exists (read-only).
|
|
670
|
+
# Required for AWS SSO/ADFS authentication: credentials for a named profile
|
|
671
|
+
# (created via `aws configure sso`) are resolved from ~/.aws/config, and the
|
|
672
|
+
# short-lived SSO token is cached under ~/.aws/sso/cache/. Mounting this
|
|
673
|
+
# directory lets boto3/LiteLLM inside the container resolve AWS_PROFILE just
|
|
674
|
+
# as it would on the host. It also covers static shared-credential setups
|
|
675
|
+
# (~/.aws/credentials). Mounted read-only; refresh the token on the host with
|
|
676
|
+
# `aws sso login --profile <name>` when it expires.
|
|
677
|
+
aws_dir = os.path.expanduser("~/.aws")
|
|
678
|
+
if os.path.isdir(aws_dir) and aws_dir not in mounted_paths:
|
|
679
|
+
container_args.extend(["-v", f"{aws_dir}:/root/.aws:ro"])
|
|
680
|
+
mounted_paths.add(aws_dir)
|
|
681
|
+
|
|
662
682
|
# Development mode: mount local patchpal source code
|
|
663
683
|
dev_mode = False
|
|
664
684
|
if sandbox_args.dev:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|