alpiecode 7.0.6__tar.gz → 8.0.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 (43) hide show
  1. {alpiecode-7.0.6 → alpiecode-8.0.0}/PKG-INFO +1 -1
  2. {alpiecode-7.0.6 → alpiecode-8.0.0}/pyproject.toml +1 -1
  3. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/PKG-INFO +1 -1
  4. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/__init__.py +1 -1
  5. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/openai_backend.py +134 -0
  6. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/extension/alpiecode.vsix +0 -0
  7. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/orchestrator.py +22 -8
  8. {alpiecode-7.0.6 → alpiecode-8.0.0}/README.md +0 -0
  9. {alpiecode-7.0.6 → alpiecode-8.0.0}/setup.cfg +0 -0
  10. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode/__init__.py +0 -0
  11. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/SOURCES.txt +0 -0
  12. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/dependency_links.txt +0 -0
  13. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/entry_points.txt +0 -0
  14. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/requires.txt +0 -0
  15. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/top_level.txt +0 -0
  16. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/agent.py +0 -0
  17. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/__init__.py +0 -0
  18. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/base.py +0 -0
  19. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/local_backend.py +0 -0
  20. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/cache.py +0 -0
  21. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/cli.py +0 -0
  22. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/client.py +0 -0
  23. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/compaction.py +0 -0
  24. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/config.py +0 -0
  25. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/context.py +0 -0
  26. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/discovery.py +0 -0
  27. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/doctor.py +0 -0
  28. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/executor.py +0 -0
  29. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/git_ops.py +0 -0
  30. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/github.py +0 -0
  31. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/guardian.py +0 -0
  32. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/ipython_ext.py +0 -0
  33. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/local_model.py +0 -0
  34. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/media.py +0 -0
  35. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/memory.py +0 -0
  36. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/progress.py +0 -0
  37. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/prompt.py +0 -0
  38. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/rephraser.py +0 -0
  39. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/server.py +0 -0
  40. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/session.py +0 -0
  41. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/tools.py +0 -0
  42. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/updater.py +0 -0
  43. {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/vscode_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 7.0.6
3
+ Version: 8.0.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 = "7.0.6"
7
+ version = "8.0.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: 7.0.6
3
+ Version: 8.0.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
@@ -16,7 +16,7 @@ Supports:
16
16
  from pathlib import Path
17
17
  from typing import Optional, Any, Dict
18
18
 
19
- __version__ = "7.0.5"
19
+ __version__ = "7.0.7"
20
20
 
21
21
 
22
22
  def run(task: str, workdir: str = ".", reasoning_level: str = "thinking", **kwargs):
@@ -55,6 +55,140 @@ class OpenAIBackend:
55
55
  pass
56
56
  return self._cfg.model
57
57
 
58
+ def chat_completion_stream(
59
+ self,
60
+ messages: List[dict],
61
+ tools: Optional[List[dict]] = None,
62
+ temperature: Optional[float] = None,
63
+ max_tokens: Optional[int] = None,
64
+ enable_thinking: bool = True,
65
+ ):
66
+ """
67
+ True real-time SSE streaming completion yielding progressive thinking deltas,
68
+ duration, content tokens, and final ChatResponse.
69
+ """
70
+ import time
71
+ import json
72
+ start_time = time.time()
73
+ temp = temperature if temperature is not None else getattr(self._cfg, "temperature", 0.1)
74
+ m_tokens = max_tokens if max_tokens is not None else getattr(self._cfg, "max_tokens", 8192)
75
+
76
+ client = self._ensure_client()
77
+ model_name = self._get_available_model(client)
78
+
79
+ params = {
80
+ "model": model_name,
81
+ "messages": messages,
82
+ "temperature": temp,
83
+ "max_tokens": m_tokens,
84
+ "extra_body": {"chat_template_kwargs": {"enable_thinking": enable_thinking}},
85
+ "stream": True,
86
+ }
87
+ if tools:
88
+ params["tools"] = tools
89
+ params["tool_choice"] = "auto"
90
+
91
+ try:
92
+ response = client.chat.completions.create(**params)
93
+ except NotFoundError:
94
+ self._resolved_model = None
95
+ model_name = self._get_available_model(client)
96
+ params["model"] = model_name
97
+ response = client.chat.completions.create(**params)
98
+
99
+ in_think = enable_thinking
100
+ think_started = False
101
+ full_content = []
102
+ full_reasoning = []
103
+ tool_calls_dict = {}
104
+
105
+ for chunk in response:
106
+ if not chunk.choices:
107
+ continue
108
+ delta = chunk.choices[0].delta
109
+ if not delta:
110
+ continue
111
+
112
+ # Tool call deltas
113
+ if delta.tool_calls:
114
+ for tc in delta.tool_calls:
115
+ idx = tc.index
116
+ if idx not in tool_calls_dict:
117
+ tool_calls_dict[idx] = {
118
+ "id": tc.id or f"call_{idx}",
119
+ "name": tc.function.name if tc.function and tc.function.name else "",
120
+ "arguments": ""
121
+ }
122
+ else:
123
+ if tc.id:
124
+ tool_calls_dict[idx]["id"] = tc.id
125
+ if tc.function and tc.function.name:
126
+ tool_calls_dict[idx]["name"] += tc.function.name
127
+ if tc.function and tc.function.arguments:
128
+ tool_calls_dict[idx]["arguments"] += tc.function.arguments
129
+
130
+ # Explicit reasoning field from some models
131
+ explicit_reasoning = getattr(delta, "reasoning", None) or getattr(delta, "reasoning_content", None)
132
+ if explicit_reasoning:
133
+ if not think_started:
134
+ think_started = True
135
+ yield ("thinking_start", {"time": start_time})
136
+ full_reasoning.append(explicit_reasoning)
137
+ yield ("thinking_delta", {"delta": explicit_reasoning})
138
+
139
+ # Content deltas (contains <think>...</think> on vLLM/Qwen models)
140
+ if delta.content:
141
+ text = delta.content
142
+ if in_think:
143
+ if not think_started:
144
+ think_started = True
145
+ yield ("thinking_start", {"time": start_time})
146
+
147
+ if "</think>" in text:
148
+ before, after = text.split("</think>", 1)
149
+ if before:
150
+ full_reasoning.append(before)
151
+ yield ("thinking_delta", {"delta": before})
152
+ in_think = False
153
+ elapsed = time.time() - start_time
154
+ yield ("thinking_end", {"duration": round(elapsed, 1), "content": "".join(full_reasoning).strip()})
155
+ if after.strip():
156
+ full_content.append(after)
157
+ yield ("token", {"delta": after})
158
+ else:
159
+ clean_text = text.replace("<think>", "")
160
+ full_reasoning.append(clean_text)
161
+ yield ("thinking_delta", {"delta": clean_text})
162
+ else:
163
+ full_content.append(text)
164
+ yield ("token", {"delta": text})
165
+
166
+ # Close thinking if stream ended while still in think block
167
+ if in_think and think_started:
168
+ elapsed = time.time() - start_time
169
+ yield ("thinking_end", {"duration": round(elapsed, 1), "content": "".join(full_reasoning).strip()})
170
+
171
+ tool_calls = None
172
+ if tool_calls_dict:
173
+ tool_calls = []
174
+ for idx in sorted(tool_calls_dict.keys()):
175
+ item = tool_calls_dict[idx]
176
+ try:
177
+ args = json.loads(item["arguments"])
178
+ except Exception:
179
+ args = {}
180
+ tool_calls.append(ToolCall(id=item["id"], name=item["name"], arguments=args))
181
+
182
+ final_content = "".join(full_content).strip()
183
+ final_reasoning = "".join(full_reasoning).strip() if full_reasoning else None
184
+
185
+ yield ("done", ChatResponse(
186
+ content=final_content,
187
+ reasoning=final_reasoning,
188
+ tool_calls=tool_calls,
189
+ raw=None
190
+ ))
191
+
58
192
  def chat_completion(
59
193
  self,
60
194
  messages: List[dict],
@@ -211,13 +211,27 @@ class AgentOrchestrator:
211
211
  else:
212
212
  max_tokens = 2048 if is_offline else effective_max_tokens
213
213
 
214
- resp = self.backend.chat_completion(
215
- messages=session.context.messages,
216
- tools=active_tools if active_tools else None,
217
- temperature=cfg.temperature,
218
- max_tokens=max_tokens,
219
- enable_thinking=enable_thinking,
220
- )
214
+ if hasattr(self.backend, "chat_completion_stream") and not is_offline:
215
+ resp = None
216
+ for event_type, data in self.backend.chat_completion_stream(
217
+ messages=session.context.messages,
218
+ tools=active_tools if active_tools else None,
219
+ temperature=cfg.temperature,
220
+ max_tokens=max_tokens,
221
+ enable_thinking=enable_thinking,
222
+ ):
223
+ if event_type == "done":
224
+ resp = data
225
+ else:
226
+ yield AgentEvent(event_type, data)
227
+ else:
228
+ resp = self.backend.chat_completion(
229
+ messages=session.context.messages,
230
+ tools=active_tools if active_tools else None,
231
+ temperature=cfg.temperature,
232
+ max_tokens=max_tokens,
233
+ enable_thinking=enable_thinking,
234
+ )
221
235
  except Exception as e:
222
236
  # Online error -> fallback to local
223
237
  if not is_offline and isinstance(self.backend, OpenAIBackend):
@@ -241,7 +255,7 @@ class AgentOrchestrator:
241
255
  yield AgentEvent("error", {"error": str(e)})
242
256
  return
243
257
 
244
- if resp.reasoning:
258
+ if resp.reasoning and not (hasattr(self.backend, "chat_completion_stream") and not is_offline):
245
259
  yield AgentEvent("thinking", {"content": resp.reasoning})
246
260
 
247
261
  session.context.add_assistant_response(resp)
File without changes
File without changes