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.
- {alpiecode-7.0.6 → alpiecode-8.0.0}/PKG-INFO +1 -1
- {alpiecode-7.0.6 → alpiecode-8.0.0}/pyproject.toml +1 -1
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/PKG-INFO +1 -1
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/__init__.py +1 -1
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/openai_backend.py +134 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/extension/alpiecode.vsix +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/orchestrator.py +22 -8
- {alpiecode-7.0.6 → alpiecode-8.0.0}/README.md +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/setup.cfg +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode/__init__.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/SOURCES.txt +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/dependency_links.txt +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/entry_points.txt +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/requires.txt +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/alpiecode.egg-info/top_level.txt +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/agent.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/__init__.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/base.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/backends/local_backend.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/cache.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/cli.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/client.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/compaction.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/config.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/context.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/discovery.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/doctor.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/executor.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/git_ops.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/github.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/guardian.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/ipython_ext.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/local_model.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/media.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/memory.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/progress.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/prompt.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/rephraser.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/server.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/session.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/tools.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/updater.py +0 -0
- {alpiecode-7.0.6 → alpiecode-8.0.0}/src/codeagent/vscode_installer.py +0 -0
|
@@ -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],
|
|
Binary file
|
|
@@ -211,13 +211,27 @@ class AgentOrchestrator:
|
|
|
211
211
|
else:
|
|
212
212
|
max_tokens = 2048 if is_offline else effective_max_tokens
|
|
213
213
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|