alpiecode 10.6.9__tar.gz → 10.7.1__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.
Files changed (58) hide show
  1. {alpiecode-10.6.9 → alpiecode-10.7.1}/PKG-INFO +1 -1
  2. {alpiecode-10.6.9 → alpiecode-10.7.1}/pyproject.toml +1 -1
  3. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/alpiecode.egg-info/PKG-INFO +1 -1
  4. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/__init__.py +1 -1
  5. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/backends/local_backend.py +87 -40
  6. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/backends/openai_backend.py +1 -1
  7. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/config.py +7 -3
  8. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_non_thinking_and_local_stream.py +25 -0
  9. {alpiecode-10.6.9 → alpiecode-10.7.1}/README.md +0 -0
  10. {alpiecode-10.6.9 → alpiecode-10.7.1}/setup.cfg +0 -0
  11. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/alpiecode/__init__.py +0 -0
  12. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/alpiecode.egg-info/SOURCES.txt +0 -0
  13. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/alpiecode.egg-info/dependency_links.txt +0 -0
  14. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/alpiecode.egg-info/entry_points.txt +0 -0
  15. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/alpiecode.egg-info/requires.txt +0 -0
  16. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/alpiecode.egg-info/top_level.txt +0 -0
  17. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/agent.py +0 -0
  18. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/assessment.py +0 -0
  19. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/backends/__init__.py +0 -0
  20. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/backends/base.py +0 -0
  21. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/cache.py +0 -0
  22. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/cli.py +0 -0
  23. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/client.py +0 -0
  24. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/compaction.py +0 -0
  25. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/context.py +0 -0
  26. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/discovery.py +0 -0
  27. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/doctor.py +0 -0
  28. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/dream_rsi.py +0 -0
  29. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/executor.py +0 -0
  30. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/extension/alpiecode.vsix +0 -0
  31. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/git_ops.py +0 -0
  32. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/github.py +0 -0
  33. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/guardian.py +0 -0
  34. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/guardrails.py +0 -0
  35. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/ipython_ext.py +0 -0
  36. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/local_model.py +0 -0
  37. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/media.py +0 -0
  38. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/memory.py +0 -0
  39. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/metrics.py +0 -0
  40. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/orchestrator.py +0 -0
  41. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/paths.py +0 -0
  42. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/progress.py +0 -0
  43. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/prompt.py +0 -0
  44. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/rephraser.py +0 -0
  45. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/server.py +0 -0
  46. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/session.py +0 -0
  47. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/system1.py +0 -0
  48. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/tools.py +0 -0
  49. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/updater.py +0 -0
  50. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/verifier.py +0 -0
  51. {alpiecode-10.6.9 → alpiecode-10.7.1}/src/codeagent/vscode_installer.py +0 -0
  52. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_adaptive_resilience.py +0 -0
  53. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_cli_ui.py +0 -0
  54. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_context_and_trajectory.py +0 -0
  55. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_dream_rsi.py +0 -0
  56. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_local_model_download.py +0 -0
  57. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_task_assessment.py +0 -0
  58. {alpiecode-10.6.9 → alpiecode-10.7.1}/test/test_verifier.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 10.6.9
3
+ Version: 10.7.1
4
4
  Summary: AlpieCode
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: huggingface_hub>=0.20.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "alpiecode"
7
- version = "10.6.9"
7
+ version = "10.7.1"
8
8
  description = "AlpieCode"
9
9
  requires-python = ">=3.9"
10
10
  dependencies = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 10.6.9
3
+ Version: 10.7.1
4
4
  Summary: AlpieCode
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: huggingface_hub>=0.20.0
@@ -17,7 +17,7 @@ Supports:
17
17
  from pathlib import Path
18
18
  from typing import Optional, Any, Dict
19
19
 
20
- __version__ = "10.6.9"
20
+ __version__ = "10.7.1"
21
21
 
22
22
  def run(task: str, workdir: str = ".", reasoning_level: str = "thinking", **kwargs):
23
23
  """Run an autonomous coding task programmatically."""
@@ -877,7 +877,7 @@ class iOSBackend(BasePlatformBackend):
877
877
  if tools:
878
878
  payload["tools"] = tools
879
879
  payload["tool_choice"] = "auto"
880
- resp = httpx.post(f"{bridge_url}/v1/chat/completions", json=payload, timeout=90.0)
880
+ resp = httpx.post(f"{bridge_url}/v1/chat/completions", json=payload, timeout=300.0)
881
881
  if resp.status_code != 200:
882
882
  raise RuntimeError(f"iOS bridge HTTP {resp.status_code}: {resp.text}")
883
883
  data = json.loads(resp.text)
@@ -956,7 +956,7 @@ class iOSBackend(BasePlatformBackend):
956
956
  if tools:
957
957
  payload["tools"] = tools
958
958
  payload["tool_choice"] = "auto"
959
- client = httpx.Client(timeout=90.0)
959
+ client = httpx.Client(timeout=httpx.Timeout(300.0, connect=10.0, read=300.0, write=30.0))
960
960
  req = client.build_request("POST", f"{bridge_url}/v1/chat/completions", json=payload)
961
961
  resp = client.send(req, stream=True)
962
962
  if resp.status_code != 200:
@@ -1007,27 +1007,63 @@ class AndroidBackend(BasePlatformBackend):
1007
1007
  def _get_bridge_url(self) -> str:
1008
1008
  return os.environ.get("ANDROID_LLAMA_BRIDGE", "http://127.0.0.1:8080").rstrip("/")
1009
1009
 
1010
+ def _find_llama_server_bin(self) -> Optional[Path]:
1011
+ custom = os.environ.get("LLAMA_SERVER_PATH") or os.environ.get("LLAMA_BIN")
1012
+ if custom and Path(custom).is_file():
1013
+ return Path(custom)
1014
+ which_bin = shutil.which("llama-server")
1015
+ if which_bin:
1016
+ return Path(which_bin)
1017
+ candidates = [
1018
+ Path("/data/data/com.termux/files/usr/bin/llama-server"),
1019
+ Path.home() / "llama.cpp" / "build" / "bin" / "llama-server",
1020
+ Path.home() / "llama.cpp" / "build" / "bin" / "Release" / "llama-server",
1021
+ Path.home() / ".alpiecode" / "bin" / "llama-server",
1022
+ Path.cwd() / "llama.cpp" / "build" / "bin" / "llama-server",
1023
+ ]
1024
+ for c in candidates:
1025
+ if c.is_file() and os.access(c, os.X_OK):
1026
+ return c
1027
+ return None
1028
+
1029
+ def _find_gguf_model(self) -> Optional[Path]:
1030
+ from ..local_model import get_model_cache_dir
1031
+ candidate_dirs = [
1032
+ get_model_cache_dir(),
1033
+ Path.home() / ".alpiecode" / "models",
1034
+ Path.home() / "models",
1035
+ Path.cwd() / "models",
1036
+ Path.cwd() / ".alpiecode" / "models",
1037
+ ]
1038
+ for d in candidate_dirs:
1039
+ if d.exists():
1040
+ ggufs = list(d.glob("*.gguf"))
1041
+ mains = [g for g in ggufs if "mmproj" not in g.name.lower()]
1042
+ if mains:
1043
+ mains.sort(key=lambda p: p.stat().st_size)
1044
+ return mains[0]
1045
+ return None
1046
+
1010
1047
  def _ensure_local_server(self) -> bool:
1011
1048
  """Auto-detect or auto-start local llama-server in background if needed."""
1012
1049
  if self._is_bridge_online():
1013
1050
  return True
1014
- server_bin = shutil.which("llama-server")
1051
+ server_bin = self._find_llama_server_bin()
1015
1052
  if not server_bin:
1053
+ self._last_server_error = "llama-server binary was not found in PATH or ~/llama.cpp/build/bin/llama-server."
1016
1054
  return False
1017
- from ..local_model import get_model_cache_dir
1018
- cache_dir = get_model_cache_dir()
1019
- if not cache_dir.exists():
1020
- return False
1021
- ggufs = list(cache_dir.glob("*.gguf"))
1022
- main_models = [g for g in ggufs if "mmproj" not in g.name.lower()]
1023
- if not main_models:
1055
+ model_path = self._find_gguf_model()
1056
+ if not model_path:
1057
+ self._last_server_error = "No GGUF model found in ~/.alpiecode/models or ~/models."
1024
1058
  return False
1025
- model_path = main_models[0]
1026
- mmprojs = [g for g in ggufs if "mmproj" in g.name.lower()]
1027
- cmd = [
1028
- server_bin,
1059
+ n_ctx = getattr(self._cfg, "n_ctx", 2048)
1060
+ if n_ctx > 2048:
1061
+ n_ctx = 2048
1062
+ n_ctx_val = str(n_ctx)
1063
+ base_cmd = [
1064
+ str(server_bin),
1029
1065
  "-m", str(model_path),
1030
- "-c", "4096",
1066
+ "-c", n_ctx_val,
1031
1067
  "-t", "4",
1032
1068
  "-np", "1",
1033
1069
  "-b", "512",
@@ -1036,9 +1072,21 @@ class AndroidBackend(BasePlatformBackend):
1036
1072
  "--cache-type-v", "q8_0",
1037
1073
  "--port", "8080",
1038
1074
  ]
1039
- if mmprojs:
1040
- cmd += ["--mmproj", str(mmprojs[0])]
1075
+ # Only load vision projector if specifically needed to keep RAM footprint under 5GB
1076
+ if getattr(self._cfg, "enable_vision", False):
1077
+ mmprojs = list(model_path.parent.glob("*mmproj*.gguf"))
1078
+ if mmprojs:
1079
+ base_cmd += ["--mmproj", str(mmprojs[0])]
1080
+
1081
+ taskset_bin = shutil.which("taskset")
1082
+ if taskset_bin and os.cpu_count() == 8:
1083
+ cmd = [taskset_bin, "-c", "4-7"] + base_cmd
1084
+ else:
1085
+ cmd = base_cmd
1086
+
1041
1087
  try:
1088
+ print(f"⏳ Starting Alpie ({model_path.name}) in background...")
1089
+ print(f" Context window: {n_ctx_val} tokens | Performance cores: 4 | Lean KV cache active")
1042
1090
  subprocess.Popen(
1043
1091
  cmd,
1044
1092
  stdout=subprocess.DEVNULL,
@@ -1046,12 +1094,13 @@ class AndroidBackend(BasePlatformBackend):
1046
1094
  start_new_session=True,
1047
1095
  )
1048
1096
  import time
1049
- for _ in range(25):
1097
+ for _ in range(40):
1050
1098
  if self._is_bridge_online():
1051
1099
  return True
1052
- time.sleep(0.4)
1053
- except Exception:
1054
- pass
1100
+ time.sleep(0.5)
1101
+ except Exception as e:
1102
+ self._last_server_error = f"Failed to launch llama-server: {e}"
1103
+ return False
1055
1104
  return self._is_bridge_online()
1056
1105
 
1057
1106
  @property
@@ -1060,7 +1109,21 @@ class AndroidBackend(BasePlatformBackend):
1060
1109
  import llama_cpp
1061
1110
  return True
1062
1111
  except ImportError:
1063
- return self._is_bridge_online() or bool(shutil.which("llama-server") or shutil.which("llama-cli"))
1112
+ return self._is_bridge_online() or bool(self._find_llama_server_bin() or shutil.which("llama-cli"))
1113
+
1114
+ def _raise_missing_engine_error(self) -> None:
1115
+ err_detail = getattr(self, "_last_server_error", None)
1116
+ detail_msg = f"\n│ Reason: {err_detail}" if err_detail else ""
1117
+ raise RuntimeError(
1118
+ "\n╭────────────────────────────────────────────────────────────╮\n"
1119
+ "│ Android / Termux Local Inference: │\n"
1120
+ "│ Local inference server (http://127.0.0.1:8080) is offline.│"
1121
+ f"{detail_msg}\n"
1122
+ "│ │\n"
1123
+ "│ To start it manually in Termux: │\n"
1124
+ "│ llama-server -m ~/.alpiecode/models/*.gguf -c 2048 -t 4 │\n"
1125
+ "╰────────────────────────────────────────────────────────────╯"
1126
+ )
1064
1127
 
1065
1128
  def chat_completion(
1066
1129
  self,
@@ -1079,15 +1142,7 @@ class AndroidBackend(BasePlatformBackend):
1079
1142
  if self._ensure_local_server():
1080
1143
  return self._bridge_chat_completion(messages, tools, temperature, max_tokens, enable_thinking)
1081
1144
 
1082
- raise RuntimeError(
1083
- "\n╭────────────────────────────────────────────────────────────╮\n"
1084
- "│ Android / Termux Native Inference: │\n"
1085
- "│ No local engine is available. │\n"
1086
- "│ To enable local inference on Termux, run: │\n"
1087
- "│ pkg install llama-cpp │\n"
1088
- "│ alpiecode init │\n"
1089
- "╰────────────────────────────────────────────────────────────╯"
1090
- )
1145
+ self._raise_missing_engine_error()
1091
1146
 
1092
1147
  def chat_completion_stream(
1093
1148
  self,
@@ -1108,15 +1163,7 @@ class AndroidBackend(BasePlatformBackend):
1108
1163
  yield from self._bridge_chat_completion_stream(messages, tools, temperature, max_tokens, enable_thinking)
1109
1164
  return
1110
1165
 
1111
- raise RuntimeError(
1112
- "\n╭────────────────────────────────────────────────────────────╮\n"
1113
- "│ Android / Termux Native Inference: │\n"
1114
- "│ No local engine is available. │\n"
1115
- "│ To enable local inference on Termux, run: │\n"
1116
- "│ pkg install llama-cpp │\n"
1117
- "│ alpiecode init │\n"
1118
- "╰────────────────────────────────────────────────────────────╯"
1119
- )
1166
+ self._raise_missing_engine_error()
1120
1167
 
1121
1168
 
1122
1169
  class MacOSBackend(BasePlatformBackend):
@@ -180,7 +180,7 @@ class _HTTPXClient:
180
180
  elif "127.0.0.1" in base_url or "localhost" in base_url:
181
181
  self.http_client = httpx.Client(
182
182
  http2=False,
183
- timeout=httpx.Timeout(180.0, connect=5.0, read=180.0, write=30.0),
183
+ timeout=httpx.Timeout(300.0, connect=10.0, read=300.0, write=30.0),
184
184
  limits=httpx.Limits(max_keepalive_connections=5, max_connections=10),
185
185
  )
186
186
  else:
@@ -233,9 +233,13 @@ def load_config() -> Config:
233
233
  except ValueError:
234
234
  pass
235
235
 
236
- # For offline mode, set high-performance 16k context (16384 tokens) for big multi-file projects
237
- # Fits completely in 6GB VRAM on RTX 3050 with zero paging
238
- if data.get("base_url") == "offline" and not any(os.environ.get(v) for v in ("ALPIECODE_N_CTX", "ALPIECODE_CTX_SIZE")):
236
+ # On mobile (Android / Termux), keep context at 2048 to prevent ZRAM thrashing with 9B models
237
+ if is_android() and not any(os.environ.get(v) for v in ("ALPIECODE_N_CTX", "ALPIECODE_CTX_SIZE")):
238
+ if data.get("n_ctx", 32768) > 2048:
239
+ data["n_ctx"] = 2048
240
+ if data.get("max_tokens", 8192) > 1024:
241
+ data["max_tokens"] = 1024
242
+ elif data.get("base_url") == "offline" and not any(os.environ.get(v) for v in ("ALPIECODE_N_CTX", "ALPIECODE_CTX_SIZE")):
239
243
  if data.get("n_ctx") in (32768, 4096, 16384):
240
244
  data["n_ctx"] = 8192
241
245
  if data.get("max_tokens", 8192) > 4096:
@@ -134,3 +134,28 @@ def test_prompt_builder_lean_offline_prompt():
134
134
  assert "You are running in OFFLINE mode" in prompt
135
135
  assert "Execution & Goal Convergence Rules" not in prompt
136
136
  assert len(prompt) < 3500 # Lean offline prompt under 3500 chars (~600 tokens vs 4500 cloud tokens)
137
+
138
+
139
+ def test_android_backend_skips_mmproj_for_code_tasks(tmp_path):
140
+ from pathlib import Path
141
+ from codeagent.backends.local_backend import AndroidBackend
142
+ cfg = Config(base_url="offline")
143
+ backend = AndroidBackend(cfg)
144
+
145
+ fake_model = tmp_path / "alpie-vl-text-q3_k_m.gguf"
146
+ fake_model.touch()
147
+ fake_mmproj = tmp_path / "alpie-vl-mmproj-f16.gguf"
148
+ fake_mmproj.touch()
149
+
150
+ with patch.object(backend, "_find_llama_server_bin", return_value=Path("/bin/llama-server")):
151
+ with patch.object(backend, "_find_gguf_model", return_value=fake_model):
152
+ with patch("subprocess.Popen") as mock_popen:
153
+ with patch.object(backend, "_is_bridge_online", side_effect=[False, True]):
154
+ backend._ensure_local_server()
155
+ assert mock_popen.called
156
+ args = mock_popen.call_args[0][0]
157
+ # By default (for code tasks), --mmproj must NOT be loaded
158
+ assert "--mmproj" not in args
159
+ assert "-c" in args
160
+ assert "2048" in args
161
+
File without changes
File without changes