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.
- {alpiecode-0.8.1 → alpiecode-0.9.0}/PKG-INFO +1 -1
- {alpiecode-0.8.1 → alpiecode-0.9.0}/pyproject.toml +1 -1
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/PKG-INFO +1 -1
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/SOURCES.txt +1 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/agent.py +37 -14
- alpiecode-0.9.0/src/codeagent/client.py +131 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/README.md +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/setup.cfg +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/dependency_links.txt +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/entry_points.txt +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/requires.txt +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/alpiecode.egg-info/top_level.txt +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/__init__.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/__init__.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/base.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/local_backend.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/backends/openai_backend.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/cli.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/compaction.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/config.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/context.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/executor.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/github.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/guardian.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/local_model.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/media.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/memory.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/orchestrator.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/prompt.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/server.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/session.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/tools.py +0 -0
- {alpiecode-0.8.1 → alpiecode-0.9.0}/src/codeagent/updater.py +0 -0
|
@@ -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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
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
|
|
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
|