Skip to content

Commit ded91de

Browse files
committed
streaming should be part of model capabilities
1 parent c51afdf commit ded91de

File tree

10 files changed

+44
-54
lines changed

10 files changed

+44
-54
lines changed

AgentCrew/modules/custom_llm/deepinfra_service.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def __init__(self):
1414
api_key=api_key,
1515
base_url="https://api.deepinfra.com/v1/openai",
1616
provider_name="deepinfra",
17-
is_stream=True,
1817
)
1918
self.model = "Qwen/Qwen3-235B-A22B"
2019
self.current_input_tokens = 0

AgentCrew/modules/custom_llm/github_copilot_service.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ def __init__(self):
1616
api_key=api_key,
1717
base_url="https://api.githubcopilot.com",
1818
provider_name="github_copilot",
19-
is_stream=True,
2019
extra_headers={
2120
"Copilot-Integration-Id": "vscode-chat",
2221
"Editor-Plugin-Version": "CopilotChat.nvim/*",

AgentCrew/modules/custom_llm/service.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ def __init__(
1616
base_url: str,
1717
api_key: str,
1818
provider_name: str,
19-
is_stream: bool = False,
2019
extra_headers: Optional[Dict[str, str]] = None,
2120
):
2221
"""
@@ -34,7 +33,6 @@ def __init__(
3433
logger.info(
3534
f"Initialized Custom LLM Service for provider: {provider_name} at {base_url}"
3635
)
37-
self._is_stream = is_stream
3836
self.extra_headers = extra_headers
3937

4038
def format_tool_result(
@@ -168,7 +166,9 @@ async def stream_assistant_response(self, messages):
168166
if self.reasoning_effort is None:
169167
stream_params["reasoning_effort"] = "none"
170168

171-
if self._is_stream:
169+
if "stream" in ModelRegistry.get_model_capabilities(
170+
f"{self._provider_name}/{self.model}"
171+
):
172172
self._is_thinking = False
173173
return await self.client.chat.completions.create(
174174
**stream_params,
@@ -196,7 +196,9 @@ async def stream_assistant_response(self, messages):
196196
def process_stream_chunk(
197197
self, chunk, assistant_response: str, tool_uses: List[Dict]
198198
) -> Tuple[str, List[Dict], int, int, Optional[str], Optional[tuple]]:
199-
if self._is_stream:
199+
if "stream" in ModelRegistry.get_model_capabilities(
200+
f"{self._provider_name}/{self.model}"
201+
):
200202
return self._process_stream_chunk(chunk, assistant_response, tool_uses)
201203
else:
202204
return self._process_non_stream_chunk(chunk, assistant_response, tool_uses)

AgentCrew/modules/groq/service.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def __init__(self):
3838
self.current_output_tokens = 0
3939
self.system_prompt = ""
4040
self.temperature = 0.4
41-
self._is_stream = False
4241
logger.info("Initialized Groq Service")
4342

4443
def set_think(self, budget_tokens) -> bool:

AgentCrew/modules/gui/components/message_handlers.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self, chat_window):
99

1010
if isinstance(chat_window, ChatWindow):
1111
self.chat_window = chat_window
12-
self.thinking_content = ""
12+
self.chat_window.thinking_content = ""
1313
self.chunk_buffer_queue = []
1414
self.think_buffer_queue = []
1515

@@ -85,13 +85,12 @@ def handle_thinking_started(self, data):
8585
self.chat_window.current_thinking_bubble = (
8686
self.chat_window.chat_components.append_thinking_message("", agent_name)
8787
)
88-
self.thinking_content = "" # Initialize thinking content
89-
# self.chat_window.thinking_buffer = "" # Initialize thinking buffer
88+
self.chat_window.thinking_content = "" # Initialize thinking content
9089

9190
def handle_thinking_chunk(self, chunk):
9291
"""Handle a chunk of the thinking process."""
9392
self.think_buffer_queue.extend(list(chunk))
94-
self.thinking_content += chunk
93+
self.chat_window.thinking_content += chunk
9594
# Use smooth streaming for thinking chunks too
9695
if self.chat_window.current_thinking_bubble:
9796
self.chat_window.current_thinking_bubble.add_streaming_chunk(
@@ -104,12 +103,13 @@ def handle_thinking_completed(self):
104103
# Finalize thinking stream if active
105104
if self.chat_window.current_thinking_bubble:
106105
self.chat_window.current_thinking_bubble.raw_text_buffer = (
107-
self.thinking_content
106+
self.chat_window.thinking_content
108107
)
109108
self.chat_window.current_thinking_bubble._finalize_streaming()
110109
# Reset thinking bubble reference
111110
self.think_buffer_queue = []
112111
self.chat_window.current_thinking_bubble = None
112+
self.chat_window.thinking_content = ""
113113

114114
def handle_user_context_request(self):
115115
"""Handle user context request."""

AgentCrew/modules/gui/components/tool_handlers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ def handle_tool_error(self, data: Dict):
8888
self.chat_window.current_response_bubble = None
8989
self.chat_window.current_response_container = None
9090

91+
if self.chat_window.current_thinking_bubble:
92+
self.chat_window.current_thinking_bubble.raw_text_buffer = (
93+
self.chat_window.thinking_content
94+
)
95+
self.chat_window.current_thinking_bubble._finalize_streaming()
96+
self.chat_window.current_thinking_bubble = None
97+
self.chat_window.thinking_content = ""
98+
9199
def handle_tool_confirmation_required(self, tool_info):
92100
"""Display a dialog for tool confirmation request."""
93101
tool_use = tool_info.copy()

AgentCrew/modules/gui/widgets/configs/custom_llm_provider.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __init__(
2828
self,
2929
provider_name: str,
3030
model_data: Optional[Dict[str, Any]] = None,
31-
existing_model_ids: List[str] = None,
31+
existing_model_ids: Optional[List[str]] = None,
3232
parent=None,
3333
):
3434
super().__init__(parent)
@@ -61,6 +61,7 @@ def __init__(
6161
self.capabilities_tool_use_checkbox = QCheckBox("Tool Use")
6262
self.capabilities_thinking_checkbox = QCheckBox("Thinking")
6363
self.capabilities_vision_checkbox = QCheckBox("Vision")
64+
self.capabilities_stream_checkbox = QCheckBox("Stream")
6465

6566
self.input_price_edit = QDoubleSpinBox()
6667
self.input_price_edit.setDecimals(6) # Increased precision
@@ -83,6 +84,7 @@ def __init__(
8384
capabilities_layout.addWidget(self.capabilities_tool_use_checkbox)
8485
capabilities_layout.addWidget(self.capabilities_thinking_checkbox)
8586
capabilities_layout.addWidget(self.capabilities_vision_checkbox)
87+
capabilities_layout.addWidget(self.capabilities_stream_checkbox)
8688
capabilities_layout.addStretch() # To push checkboxes to the left
8789
form_layout.addRow("Capabilities:", capabilities_layout)
8890

@@ -115,6 +117,7 @@ def populate_fields(self, data: Dict[str, Any]):
115117
"thinking" in current_capabilities
116118
)
117119
self.capabilities_vision_checkbox.setChecked("vision" in current_capabilities)
120+
self.capabilities_stream_checkbox.setChecked("stream" in current_capabilities)
118121

119122
self.input_price_edit.setValue(data.get("input_token_price_1m", 0.0))
120123
self.output_price_edit.setValue(data.get("output_token_price_1m", 0.0))
@@ -130,11 +133,12 @@ def get_model_data(self) -> Dict[str, Any]:
130133
capabilities_list.append("thinking")
131134
if self.capabilities_vision_checkbox.isChecked():
132135
capabilities_list.append("vision")
136+
if self.capabilities_stream_checkbox.isChecked():
137+
capabilities_list.append("stream")
133138

134139
# Ensure Model Pydantic types are respected
135140
return {
136141
"id": self.id_edit.text().strip(),
137-
"provider": self.provider_name, # Name of the custom provider
138142
"name": self.name_edit.text().strip(),
139143
"description": self.description_edit.toPlainText().strip(),
140144
"capabilities": capabilities_list,
@@ -254,13 +258,11 @@ def clear_and_disable_form(self):
254258
self.api_base_url_edit.clear()
255259
self.api_key_edit.clear()
256260
self.default_model_id_edit.clear()
257-
self.is_stream_checkbox.setChecked(False)
258261

259262
self.name_edit.setEnabled(False)
260263
self.api_base_url_edit.setEnabled(False)
261264
self.api_key_edit.setEnabled(False)
262265
self.default_model_id_edit.setEnabled(False)
263-
self.is_stream_checkbox.setEnabled(False)
264266

265267
self.save_button.setEnabled(False)
266268
self.remove_button.setEnabled(False)
@@ -294,7 +296,6 @@ def on_provider_selected(self, current_item, previous_item):
294296
self.api_base_url_edit.setText(provider_data.get("api_base_url", ""))
295297
self.api_key_edit.setText(provider_data.get("api_key", ""))
296298
self.default_model_id_edit.setText(provider_data.get("default_model_id", ""))
297-
self.is_stream_checkbox.setChecked(provider_data.get("is_stream", False))
298299

299300
# Clear and reload header fields
300301
self.clear_header_fields()
@@ -311,7 +312,6 @@ def on_provider_selected(self, current_item, previous_item):
311312
self.api_base_url_edit.setEnabled(True)
312313
self.api_key_edit.setEnabled(True)
313314
self.default_model_id_edit.setEnabled(True)
314-
self.is_stream_checkbox.setEnabled(True)
315315

316316
self.save_button.setEnabled(True)
317317
self.remove_button.setEnabled(True)
@@ -358,9 +358,6 @@ def add_new_provider_triggered(self):
358358
self.default_model_id_edit.setEnabled(True)
359359
self.default_model_id_edit.clear()
360360

361-
self.is_stream_checkbox.setEnabled(True)
362-
self.is_stream_checkbox.setChecked(False)
363-
364361
# Clear existing headers and enable adding new ones
365362
self.clear_header_fields()
366363
self.add_header_button.setEnabled(True)
@@ -425,14 +422,12 @@ def init_ui(self):
425422
self.api_base_url_edit = QLineEdit()
426423
self.api_key_edit = QLineEdit()
427424
self.default_model_id_edit = QLineEdit()
428-
self.is_stream_checkbox = QCheckBox("Enable Streaming")
429425

430426
form_layout.addRow("Name:", self.name_edit)
431427
form_layout.addRow("Type:", self.type_display)
432428
form_layout.addRow("Base URL:", self.api_base_url_edit)
433429
form_layout.addRow("API Key:", self.api_key_edit)
434430
form_layout.addRow("Default Model ID:", self.default_model_id_edit)
435-
form_layout.addRow("Streaming:", self.is_stream_checkbox)
436431

437432
editor_layout.addLayout(form_layout)
438433

@@ -501,7 +496,6 @@ def init_ui(self):
501496
self.api_base_url_edit.setEnabled(False)
502497
self.api_key_edit.setEnabled(False)
503498
self.default_model_id_edit.setEnabled(False)
504-
self.is_stream_checkbox.setEnabled(False)
505499
self.save_button.setEnabled(False)
506500

507501
self.setLayout(main_layout)
@@ -608,7 +602,6 @@ def save_provider_details(self):
608602
api_base_url = self.api_base_url_edit.text().strip()
609603
api_key = self.api_key_edit.text().strip()
610604
default_model_id = self.default_model_id_edit.text().strip()
611-
is_stream = self.is_stream_checkbox.isChecked()
612605

613606
if not name or not api_base_url:
614607
QMessageBox.warning(
@@ -645,7 +638,6 @@ def save_provider_details(self):
645638
"api_key": api_key,
646639
"default_model_id": default_model_id,
647640
"available_models": available_models_data, # List of model dictionaries
648-
"is_stream": is_stream,
649641
"extra_headers": extra_headers, # Add the extra_headers field
650642
}
651643

AgentCrew/modules/llm/constants.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@
205205
provider="deepinfra",
206206
name="Llama 3.3 70B Instruct",
207207
description="Llama 3.3-70B is a multilingual LLM trained on a massive dataset of 15 trillion tokens, fine-tuned for instruction-following and conversational dialogue",
208-
capabilities=["tool_use", "text-generation"],
208+
capabilities=["tool_use", "text-generation", "stream"],
209209
input_token_price_1m=0.23,
210210
output_token_price_1m=0.40,
211211
),
@@ -214,7 +214,7 @@
214214
provider="deepinfra",
215215
name="Gemma 3 27B",
216216
description="Gemma is a family of lightweight, state-of-the-art open models from Google, built from the same research and technology used to create the Gemini models",
217-
capabilities=["text-generation", "tool_use"],
217+
capabilities=["text-generation", "tool_use", "stream"],
218218
input_token_price_1m=0.1,
219219
output_token_price_1m=0.2,
220220
),
@@ -241,7 +241,7 @@
241241
provider="deepinfra",
242242
name="Qwen 3 MoE 235B-22B",
243243
description="Qwen3 is the latest generation of large language models in Qwen series, offering a comprehensive suite of dense and mixture-of-experts (MoE) models",
244-
capabilities=["text-generation", "tool_use", "thinking"],
244+
capabilities=["text-generation", "tool_use", "thinking", "stream"],
245245
input_token_price_1m=0.2,
246246
output_token_price_1m=0.6,
247247
),
@@ -250,7 +250,7 @@
250250
provider="deepinfra",
251251
name="Qwen 3 32B",
252252
description="Qwen3 is the latest generation of large language models in Qwen series, offering a comprehensive suite of dense and mixture-of-experts (MoE) models",
253-
capabilities=["text-generation", "tool_use"],
253+
capabilities=["text-generation", "tool_use", "stream"],
254254
input_token_price_1m=0.1,
255255
output_token_price_1m=0.3,
256256
),
@@ -268,7 +268,7 @@
268268
provider="deepinfra",
269269
name="DeepSeek R1 0528",
270270
description="The DeepSeek R1 model has undergone a minor version upgrade, with the current version being DeepSeek-R1-0528.",
271-
capabilities=["text-generation", "tool_use", "thinking"],
271+
capabilities=["text-generation", "tool_use", "thinking", "stream"],
272272
input_token_price_1m=0.5,
273273
output_token_price_1m=2.18,
274274
),
@@ -279,7 +279,7 @@
279279
provider="github_copilot",
280280
name="claude-3.7-sonnet",
281281
description="",
282-
capabilities=["tool_use", "vision"],
282+
capabilities=["tool_use", "vision", "stream"],
283283
default=False,
284284
input_token_price_1m=0.0,
285285
output_token_price_1m=0.0,
@@ -289,7 +289,7 @@
289289
provider="github_copilot",
290290
name="gemini-2.5-pro-preview-05-06",
291291
description="",
292-
capabilities=["tool_use", "vision"],
292+
capabilities=["tool_use", "vision", "stream"],
293293
default=False,
294294
input_token_price_1m=0.0,
295295
output_token_price_1m=0.0,
@@ -299,7 +299,7 @@
299299
provider="github_copilot",
300300
name="claude-3.7-sonnet-thought",
301301
description="",
302-
capabilities=["tool_use", "thinking", "vision"],
302+
capabilities=["tool_use", "thinking", "vision", "stream"],
303303
default=False,
304304
input_token_price_1m=0.0,
305305
output_token_price_1m=0.0,
@@ -309,7 +309,7 @@
309309
provider="github_copilot",
310310
name="gpt-4.1",
311311
description="",
312-
capabilities=["tool_use", "vision"],
312+
capabilities=["tool_use", "vision", "stream"],
313313
default=True,
314314
input_token_price_1m=0.0,
315315
output_token_price_1m=0.0,
@@ -319,7 +319,7 @@
319319
provider="github_copilot",
320320
name="o4-mini",
321321
description="",
322-
capabilities=["tool_use", "thinking"],
322+
capabilities=["tool_use", "thinking", "stream"],
323323
default=False,
324324
input_token_price_1m=0.0,
325325
output_token_price_1m=0.0,
@@ -329,7 +329,7 @@
329329
provider="github_copilot",
330330
name="o1",
331331
description="",
332-
capabilities=["tool_use", "thinking", "vision"],
332+
capabilities=["tool_use", "thinking", "vision", "stream"],
333333
default=False,
334334
input_token_price_1m=0.0,
335335
output_token_price_1m=0.0,
@@ -339,7 +339,7 @@
339339
provider="github_copilot",
340340
name="claude-sonnet-4",
341341
description="",
342-
capabilities=["tool_use", "vision"],
342+
capabilities=["tool_use", "vision", "stream"],
343343
default=False,
344344
input_token_price_1m=0.0,
345345
output_token_price_1m=0.0,

AgentCrew/modules/llm/model_registry.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,13 @@ def _load_custom_models_from_config(self):
4747
provider_name = provider_config.get("name")
4848
for model_data_dict in provider_config.get("available_models", []):
4949
try:
50-
# Ensure provider name from the outer config is used if not in model_data_dict
51-
if "provider" not in model_data_dict or not model_data_dict.get(
52-
"provider"
53-
):
54-
if provider_name:
55-
model_data_dict["provider"] = provider_name
56-
else:
57-
print(
58-
f"Warning: Skipping model due to missing provider name in config: ID '{model_data_dict.get('id', 'N/A')}'"
59-
)
60-
continue
50+
if provider_name:
51+
model_data_dict["provider"] = provider_name
52+
else:
53+
print(
54+
f"Warning: Skipping model due to missing provider name in config: ID '{model_data_dict.get('id', 'N/A')}'"
55+
)
56+
continue
6157
model = Model(**model_data_dict)
6258
self.register_model(model)
6359
except Exception as e:

0 commit comments

Comments
 (0)