alpiecode 0.8.2__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.2 → alpiecode-0.9.0}/PKG-INFO +1 -1
  2. {alpiecode-0.8.2 → alpiecode-0.9.0}/pyproject.toml +1 -1
  3. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/alpiecode.egg-info/PKG-INFO +1 -1
  4. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/alpiecode.egg-info/SOURCES.txt +1 -0
  5. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/agent.py +36 -14
  6. alpiecode-0.9.0/src/codeagent/client.py +131 -0
  7. {alpiecode-0.8.2 → alpiecode-0.9.0}/README.md +0 -0
  8. {alpiecode-0.8.2 → alpiecode-0.9.0}/setup.cfg +0 -0
  9. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/alpiecode.egg-info/dependency_links.txt +0 -0
  10. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/alpiecode.egg-info/entry_points.txt +0 -0
  11. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/alpiecode.egg-info/requires.txt +0 -0
  12. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/alpiecode.egg-info/top_level.txt +0 -0
  13. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/__init__.py +0 -0
  14. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/backends/__init__.py +0 -0
  15. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/backends/base.py +0 -0
  16. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/backends/local_backend.py +0 -0
  17. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/backends/openai_backend.py +0 -0
  18. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/cli.py +0 -0
  19. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/compaction.py +0 -0
  20. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/config.py +0 -0
  21. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/context.py +0 -0
  22. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/executor.py +0 -0
  23. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/github.py +0 -0
  24. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/guardian.py +0 -0
  25. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/local_model.py +0 -0
  26. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/media.py +0 -0
  27. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/memory.py +0 -0
  28. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/orchestrator.py +0 -0
  29. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/prompt.py +0 -0
  30. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/server.py +0 -0
  31. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/session.py +0 -0
  32. {alpiecode-0.8.2 → alpiecode-0.9.0}/src/codeagent/tools.py +0 -0
  33. {alpiecode-0.8.2 → 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.2
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.2"
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.2
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
@@ -149,28 +149,50 @@ def run_agent(
149
149
  video_path: str = None,
150
150
  url: str = None,
151
151
  github_repo: str = None,
152
+ server_url: str = None,
152
153
  ) -> list:
153
154
  """Run non-interactive agent task with Rich presentation."""
154
155
  workdir = workdir.resolve()
155
156
  _ensure_git(workdir)
156
157
 
157
- backend = resolve_backend(cfg)
158
- orchestrator = AgentOrchestrator(backend)
159
- session_mgr = SessionManager()
160
- 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
+ )
161
191
 
162
192
  _checkpoint(workdir, "checkpoint: start")
163
193
 
164
194
  current_turn = 0
165
- for event in orchestrator.run_task(
166
- session=session,
167
- task=task,
168
- cfg=cfg,
169
- image_path=image_path,
170
- video_path=video_path,
171
- url=url,
172
- github_repo=github_repo,
173
- ):
195
+ for event in event_stream:
174
196
  if event.type == "start" and verbose:
175
197
  data = event.data
176
198
  if HAS_RICH:
@@ -254,7 +276,7 @@ def run_agent(
254
276
  elif event.type == "max_turns_reached" and verbose:
255
277
  console.print(f"\n⚠️ Max turns ({event.data['max_turns']}) reached without completion.", style="bold yellow")
256
278
 
257
- return session.context.messages
279
+ return session.context.messages if "session" in locals() else []
258
280
 
259
281
 
260
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