alpiecode 0.8.1__tar.gz → 0.9.0__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 (33) hide show
  1. {alpiecode-0.8.1 → alpiecode-0.9.0}/PKG-INFO +1 -1
  2. {alpiecode-0.8.1 → alpiecode-0.9.0}/pyproject.toml +1 -1
  3. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/PKG-INFO +1 -1
  4. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/SOURCES.txt +1 -0
  5. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/agent.py +37 -14
  6. alpiecode-0.9.0/src/codeagent/client.py +131 -0
  7. {alpiecode-0.8.1 → alpiecode-0.9.0}/README.md +0 -0
  8. {alpiecode-0.8.1 → alpiecode-0.9.0}/setup.cfg +0 -0
  9. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/dependency_links.txt +0 -0
  10. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/entry_points.txt +0 -0
  11. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/requires.txt +0 -0
  12. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/top_level.txt +0 -0
  13. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/__init__.py +0 -0
  14. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/__init__.py +0 -0
  15. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/base.py +0 -0
  16. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/local_backend.py +0 -0
  17. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/openai_backend.py +0 -0
  18. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/cli.py +0 -0
  19. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/compaction.py +0 -0
  20. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/config.py +0 -0
  21. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/context.py +0 -0
  22. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/executor.py +0 -0
  23. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/github.py +0 -0
  24. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/guardian.py +0 -0
  25. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/local_model.py +0 -0
  26. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/media.py +0 -0
  27. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/memory.py +0 -0
  28. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/orchestrator.py +0 -0
  29. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/prompt.py +0 -0
  30. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/server.py +0 -0
  31. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/session.py +0 -0
  32. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/tools.py +0 -0
  33. {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/updater.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 0.8.1
3
+ Version: 0.9.0
4
4
  Summary: AlpieCode — Autonomous AI Coding Agent CLI powered by local 169Pi GGUF VLM
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 = "0.8.1"
7
+ version = "0.9.0"
8
8
  description = "AlpieCode — Autonomous AI Coding Agent CLI powered by local 169Pi GGUF VLM"
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: 0.8.1
3
+ Version: 0.9.0
4
4
  Summary: AlpieCode — Autonomous AI Coding Agent CLI powered by local 169Pi GGUF VLM
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: huggingface_hub>=0.20.0
@@ -9,6 +9,7 @@ src/alpiecode.egg-info/top_level.txt
9
9
  src/codeagent/__init__.py
10
10
  src/codeagent/agent.py
11
11
  src/codeagent/cli.py
12
+ src/codeagent/client.py
12
13
  src/codeagent/compaction.py
13
14
  src/codeagent/config.py
14
15
  src/codeagent/context.py
@@ -12,6 +12,7 @@ from typing import Any, Dict, List, Optional
12
12
  from .config import Config, is_server_reachable
13
13
  from .context import ContextManager, _serialize_assistant_message
14
14
  from .executor import READ_ONLY_TOOLS, ToolExecutor, parse_text_tool_calls as _parse_text_tool_calls
15
+ from .backends.local_backend import LocalBackend
15
16
  from .orchestrator import AgentEvent, AgentOrchestrator, resolve_backend
16
17
  from .prompt import (
17
18
  OFFLINE_SYSTEM_PROMPT,
@@ -148,28 +149,50 @@ def run_agent(
148
149
  video_path: str = None,
149
150
  url: str = None,
150
151
  github_repo: str = None,
152
+ server_url: str = None,
151
153
  ) -> list:
152
154
  """Run non-interactive agent task with Rich presentation."""
153
155
  workdir = workdir.resolve()
154
156
  _ensure_git(workdir)
155
157
 
156
- backend = resolve_backend(cfg)
157
- orchestrator = AgentOrchestrator(backend)
158
- session_mgr = SessionManager()
159
- session = session_mgr.create_session(workdir, max_tokens=cfg.n_ctx if not backend.is_available else 262_144)
158
+ # CLI-as-Client check: auto-detect if local/remote server is running
159
+ from .client import AlpieCodeClient
160
+ target_server = server_url or getattr(cfg, "server_url", None)
161
+ if not target_server:
162
+ # Check default local server
163
+ local_client = AlpieCodeClient("http://127.0.0.1:7169")
164
+ if local_client.health().get("status") == "online":
165
+ target_server = "http://127.0.0.1:7169"
166
+
167
+ if target_server:
168
+ client = AlpieCodeClient(target_server)
169
+ event_stream = client.stream_chat(
170
+ task=task,
171
+ workdir=str(workdir),
172
+ image_path=image_path,
173
+ video_path=video_path,
174
+ url=url,
175
+ github_repo=github_repo,
176
+ )
177
+ else:
178
+ backend = resolve_backend(cfg)
179
+ orchestrator = AgentOrchestrator(backend)
180
+ session_mgr = SessionManager()
181
+ session = session_mgr.create_session(workdir, max_tokens=cfg.n_ctx if not backend.is_available else 262_144)
182
+ event_stream = orchestrator.run_task(
183
+ session=session,
184
+ task=task,
185
+ cfg=cfg,
186
+ image_path=image_path,
187
+ video_path=video_path,
188
+ url=url,
189
+ github_repo=github_repo,
190
+ )
160
191
 
161
192
  _checkpoint(workdir, "checkpoint: start")
162
193
 
163
194
  current_turn = 0
164
- for event in orchestrator.run_task(
165
- session=session,
166
- task=task,
167
- cfg=cfg,
168
- image_path=image_path,
169
- video_path=video_path,
170
- url=url,
171
- github_repo=github_repo,
172
- ):
195
+ for event in event_stream:
173
196
  if event.type == "start" and verbose:
174
197
  data = event.data
175
198
  if HAS_RICH:
@@ -253,7 +276,7 @@ def run_agent(
253
276
  elif event.type == "max_turns_reached" and verbose:
254
277
  console.print(f"\n⚠️ Max turns ({event.data['max_turns']}) reached without completion.", style="bold yellow")
255
278
 
256
- return session.context.messages
279
+ return session.context.messages if "session" in locals() else []
257
280
 
258
281
 
259
282
  def run_chat(workdir: Path, cfg: Config, verbose: bool = True) -> None:
@@ -0,0 +1,131 @@
1
+ """
2
+ Client SDK for AlpieCode API server.
3
+
4
+ Provides a clean, typed interface for connecting to an AlpieCode server (`alpiecode serve`),
5
+ streaming SSE events, managing sessions, and canceling tasks.
6
+ Used by the CLI client mode and future IDE/remote Python integrations.
7
+ """
8
+
9
+ import json
10
+ import urllib.parse
11
+ import urllib.request
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from typing import Any, Dict, Iterator, Optional
15
+
16
+ from .orchestrator import AgentEvent
17
+
18
+
19
+ class AlpieCodeClient:
20
+ """Client for interacting with a running AlpieCode API server."""
21
+
22
+ def __init__(self, base_url: str = "http://127.0.0.1:7169", timeout: float = 120.0):
23
+ self.base_url = base_url.rstrip("/")
24
+ self.timeout = timeout
25
+
26
+ def health(self) -> Dict[str, Any]:
27
+ """Check server health and backend status."""
28
+ req = urllib.request.Request(f"{self.base_url}/health")
29
+ try:
30
+ with urllib.request.urlopen(req, timeout=3.0) as resp:
31
+ return json.loads(resp.read().decode("utf-8"))
32
+ except Exception as e:
33
+ return {"status": "offline", "error": str(e)}
34
+
35
+ def metrics(self) -> Dict[str, Any]:
36
+ """Get server observability metrics."""
37
+ req = urllib.request.Request(f"{self.base_url}/metrics")
38
+ try:
39
+ with urllib.request.urlopen(req, timeout=3.0) as resp:
40
+ return json.loads(resp.read().decode("utf-8"))
41
+ except Exception as e:
42
+ return {"error": str(e)}
43
+
44
+ def list_sessions(self) -> list:
45
+ """List active server sessions."""
46
+ req = urllib.request.Request(f"{self.base_url}/sessions")
47
+ try:
48
+ with urllib.request.urlopen(req, timeout=5.0) as resp:
49
+ data = json.loads(resp.read().decode("utf-8"))
50
+ return data.get("sessions", [])
51
+ except Exception:
52
+ return []
53
+
54
+ def cancel_task(self, session_id: str) -> bool:
55
+ """Request cancellation of an in-progress task session."""
56
+ req = urllib.request.Request(
57
+ f"{self.base_url}/cancel/{session_id}",
58
+ method="POST",
59
+ headers={"Content-Type": "application/json"},
60
+ )
61
+ try:
62
+ with urllib.request.urlopen(req, timeout=5.0) as resp:
63
+ return resp.status == 200
64
+ except Exception:
65
+ return False
66
+
67
+ def stream_chat(
68
+ self,
69
+ task: str,
70
+ workdir: str = ".",
71
+ session_id: Optional[str] = None,
72
+ image_path: Optional[str] = None,
73
+ video_path: Optional[str] = None,
74
+ url: Optional[str] = None,
75
+ github_repo: Optional[str] = None,
76
+ ) -> Iterator[AgentEvent]:
77
+ """
78
+ Stream agent events from POST /chat using Server-Sent Events (SSE).
79
+ Yields AgentEvent instances.
80
+ """
81
+ endpoint = f"{self.base_url}/chat"
82
+ payload = json.dumps({
83
+ "task": task,
84
+ "workdir": str(workdir),
85
+ "session_id": session_id,
86
+ "image": image_path,
87
+ "video": video_path,
88
+ "url": url,
89
+ "github": github_repo,
90
+ }).encode("utf-8")
91
+
92
+ req = urllib.request.Request(
93
+ endpoint,
94
+ data=payload,
95
+ headers={
96
+ "Content-Type": "application/json",
97
+ "Accept": "text/event-stream",
98
+ },
99
+ )
100
+
101
+ try:
102
+ with urllib.request.urlopen(req, timeout=self.timeout) as resp:
103
+ current_event_type = "message"
104
+ current_data_str = ""
105
+
106
+ for raw_line in resp:
107
+ line = raw_line.decode("utf-8").strip()
108
+ if not line:
109
+ if current_data_str:
110
+ try:
111
+ event_data = json.loads(current_data_str)
112
+ except Exception:
113
+ event_data = {"content": current_data_str}
114
+ yield AgentEvent(type=current_event_type, data=event_data)
115
+ current_event_type = "message"
116
+ current_data_str = ""
117
+ continue
118
+
119
+ if line.startswith("event:"):
120
+ current_event_type = line[6:].strip()
121
+ elif line.startswith("data:"):
122
+ current_data_str = line[5:].strip()
123
+
124
+ if current_data_str:
125
+ try:
126
+ event_data = json.loads(current_data_str)
127
+ except Exception:
128
+ event_data = {"content": current_data_str}
129
+ yield AgentEvent(type=current_event_type, data=event_data)
130
+ except Exception as e:
131
+ yield AgentEvent(type="error", data={"error": f"Server connection failed: {e}"})
File without changes
File without changes