agentcrew-ai 0.8.2__py3-none-any.whl → 0.8.3__py3-none-any.whl

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. AgentCrew/__init__.py +1 -1
  2. AgentCrew/modules/agents/local_agent.py +11 -0
  3. AgentCrew/modules/browser_automation/element_extractor.py +4 -3
  4. AgentCrew/modules/browser_automation/js/draw_element_boxes.js +200 -0
  5. AgentCrew/modules/browser_automation/js/extract_clickable_elements.js +57 -23
  6. AgentCrew/modules/browser_automation/js/extract_elements_by_text.js +21 -19
  7. AgentCrew/modules/browser_automation/js/extract_input_elements.js +22 -23
  8. AgentCrew/modules/browser_automation/js/filter_hidden_elements.js +104 -0
  9. AgentCrew/modules/browser_automation/js/remove_element_boxes.js +29 -0
  10. AgentCrew/modules/browser_automation/js_loader.py +385 -92
  11. AgentCrew/modules/browser_automation/service.py +118 -347
  12. AgentCrew/modules/browser_automation/tool.py +28 -29
  13. AgentCrew/modules/chat/message/conversation.py +9 -8
  14. AgentCrew/modules/console/input_handler.py +2 -0
  15. AgentCrew/modules/console/ui_effects.py +3 -4
  16. AgentCrew/modules/custom_llm/service.py +25 -3
  17. AgentCrew/modules/file_editing/tool.py +9 -11
  18. AgentCrew/modules/google/native_service.py +13 -0
  19. AgentCrew/modules/llm/constants.py +38 -1
  20. AgentCrew/modules/llm/model_registry.py +9 -0
  21. AgentCrew/modules/llm/types.py +12 -1
  22. AgentCrew/modules/memory/base_service.py +2 -2
  23. AgentCrew/modules/memory/chroma_service.py +80 -138
  24. AgentCrew/modules/memory/tool.py +15 -15
  25. AgentCrew/modules/openai/response_service.py +19 -11
  26. AgentCrew/modules/openai/service.py +15 -0
  27. AgentCrew/modules/prompts/constants.py +27 -14
  28. {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.3.dist-info}/METADATA +2 -2
  29. {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.3.dist-info}/RECORD +33 -30
  30. {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.3.dist-info}/WHEEL +0 -0
  31. {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.3.dist-info}/entry_points.txt +0 -0
  32. {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.3.dist-info}/licenses/LICENSE +0 -0
  33. {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.3.dist-info}/top_level.txt +0 -0
@@ -52,13 +52,11 @@ def get_browser_navigate_tool_definition(provider="claude") -> Dict[str, Any]:
52
52
 
53
53
  def get_browser_click_tool_definition(provider="claude") -> Dict[str, Any]:
54
54
  """Get tool definition for browser element clicking."""
55
- tool_description = (
56
- "Click an element using its UUID. Get UUIDs from browser_get_content first."
57
- )
55
+ tool_description = "Click an element using its UUID. Get UUIDs from get_browser_content tool result first."
58
56
  tool_arguments = {
59
57
  "element_uuid": {
60
58
  "type": "string",
61
- "description": "UUID identifier from browser_get_content clickable elements table.",
59
+ "description": "UUID identifier from get_browser_content tool result clickable elements table.",
62
60
  }
63
61
  }
64
62
  tool_required = ["element_uuid"]
@@ -109,9 +107,10 @@ def get_browser_scroll_tool_definition(provider="claude") -> Dict[str, Any]:
109
107
  "element_uuid": {
110
108
  "type": "string",
111
109
  "description": "Optional UUID for specific element to scroll. Scrolls document if not provided.",
110
+ "default": "document",
112
111
  },
113
112
  }
114
- tool_required = ["direction"]
113
+ tool_required = ["direction", "element_uuid"]
115
114
 
116
115
  if provider == "claude":
117
116
  return {
@@ -142,7 +141,7 @@ def get_browser_get_content_tool_definition(provider="claude") -> Dict[str, Any]
142
141
  """Get tool definition for browser content extraction."""
143
142
  tool_description = (
144
143
  "Extract page content as markdown with tables of clickable, input, and scrollable elements. UUIDs reset on each call."
145
- "browser_get_content result is UNIQUE in whole conversation. Remember to summarize important information before calling again."
144
+ "get_browser_content tool's result is UNIQUE in whole conversation. Remember to summarize important information before calling again."
146
145
  )
147
146
  tool_arguments = {}
148
147
  tool_required = []
@@ -196,7 +195,7 @@ def get_browser_get_content_tool_handler(
196
195
  tool_result.append(context_image.get("screenshot", {}))
197
196
  return tool_result
198
197
  else:
199
- raise RuntimeError(f"Content extraction failed: {result['error']}")
198
+ raise RuntimeError(f"Content extraction failed: {result['error']}")
200
199
 
201
200
  return handle_browser_get_content
202
201
 
@@ -221,9 +220,9 @@ def get_browser_navigate_tool_handler(
221
220
  if result.get("profile")
222
221
  else ""
223
222
  )
224
- return f"{result.get('message', 'Success')}. Use `browser_get_content` to read the url content.\nCurrent URL: {result.get('current_url', 'Unknown')}{profile_info}"
223
+ return f"{result.get('message', 'Success')}. Call `get_browser_content` tool to read the url content.\nCurrent URL: {result.get('current_url', 'Unknown')}{profile_info}"
225
224
  else:
226
- raise RuntimeError(f"Navigation failed: {result['error']}")
225
+ raise RuntimeError(f"Navigation failed: {result['error']}")
227
226
 
228
227
  return handle_browser_navigate
229
228
 
@@ -241,16 +240,15 @@ def get_browser_click_tool_handler(
241
240
 
242
241
  result = browser_service.click_element(element_uuid)
243
242
 
244
- diff_summary = _get_content_delta_changes(browser_service)
245
-
246
243
  if result.get("success", True):
244
+ diff_summary = _get_content_delta_changes(browser_service)
247
245
  return (
248
- f"{result.get('message', 'Success')}. Use `browser_get_content` to get the updated content.\n"
246
+ f"{result.get('message', 'Success')}. Call `get_browser_content` tool to get the updated content.\n"
249
247
  f"UUID: {element_uuid}\nClickedElement: {result.get('elementInfo', {}).get('text', 'Unknown')}.\n"
250
248
  f"Content delta changes:\n{diff_summary}"
251
249
  )
252
250
  else:
253
- return f"Click failed: {result['error']}\nUUID: {element_uuid}.\nUse `browser_get_content` to get the updated UUID"
251
+ return f"Click failed: {result['error']}\nUUID: {element_uuid}.\nCall `get_browser_content` tool to get the updated UUID"
254
252
 
255
253
  return handle_browser_click
256
254
 
@@ -273,24 +271,26 @@ def get_browser_scroll_tool_handler(
273
271
  "Error: Invalid scroll direction. Use 'up', 'down', 'left', or 'right'."
274
272
  )
275
273
 
276
- result = browser_service.scroll_page(direction, amount, element_uuid)
274
+ result = browser_service.scroll_page(
275
+ direction, amount, element_uuid if element_uuid != "document" else None
276
+ )
277
277
 
278
278
  if result.get("success", True):
279
- return f"{result.get('message', 'Success')}, Use `browser_get_content` to get the updated content."
279
+ return f"{result.get('message', 'Success')}, Call `get_browser_content` tool to get the updated content."
280
280
  else:
281
281
  uuid_info = f"\nUUID: {element_uuid}" if element_uuid else ""
282
- raise RuntimeError(f"Scroll failed: {result['error']}{uuid_info}")
282
+ raise RuntimeError(f"Scroll failed: {result['error']}{uuid_info}")
283
283
 
284
284
  return handle_browser_scroll
285
285
 
286
286
 
287
287
  def get_browser_input_tool_definition(provider="claude") -> Dict[str, Any]:
288
288
  """Get tool definition for browser input."""
289
- tool_description = "Input data into form fields using UUID. Get UUIDs from browser_get_content first."
289
+ tool_description = "Input data into form fields using UUID. Get UUIDs from get_browser_content tool first."
290
290
  tool_arguments = {
291
291
  "element_uuid": {
292
292
  "type": "string",
293
- "description": "UUID identifier from browser_get_content input elements table.",
293
+ "description": "UUID identifier from get_browser_content tool result's input elements table.",
294
294
  },
295
295
  "value": {
296
296
  "type": "string",
@@ -340,13 +340,13 @@ def get_browser_input_tool_handler(
340
340
  return "Error: No value provided for input."
341
341
 
342
342
  result = browser_service.input_data(element_uuid, str(value))
343
- diff_summary = _get_content_delta_changes(browser_service)
344
343
 
345
344
  if result.get("success", True):
346
- return f"✅ {result.get('message', 'Success')}\nUUID: {element_uuid}\nValue: {value}\nContent delta changes:\n{diff_summary}"
345
+ diff_summary = _get_content_delta_changes(browser_service)
346
+ return f"{result.get('message', 'Success')}\nUUID: {element_uuid}\nValue: {value}\nContent delta changes:\n{diff_summary}"
347
347
  else:
348
348
  raise RuntimeError(
349
- f"Input failed: {result['error']}\nUUID: {element_uuid}\nValue: {value}.\n Use `browser_get_content` to get updated UUID."
349
+ f"Input failed: {result['error']}\nUUID: {element_uuid}\nValue: {value}.\n Call `get_browser_content` tool to get updated UUID."
350
350
  )
351
351
 
352
352
  return handle_browser_input
@@ -405,16 +405,15 @@ def get_browser_get_elements_by_text_tool_handler(
405
405
  if result.get("success", False):
406
406
  elements_found = result.get("elements_found", 0)
407
407
  if elements_found == 0:
408
- return f"No elements found containing text: '{text}'"
408
+ return f"No elements found containing text: '{text}'"
409
409
 
410
410
  content = result.get("content", "")
411
411
  return (
412
- f"Found {elements_found} elements containing text: '{text}'\n"
413
- + content
412
+ f"Found {elements_found} elements containing text: '{text}'\n" + content
414
413
  )
415
414
  else:
416
415
  raise RuntimeError(
417
- f"Search failed: {result.get('error', 'Unknown error')}\nSearch text: '{text}'"
416
+ f"Search failed: {result.get('error', 'Unknown error')}\nSearch text: '{text}'"
418
417
  )
419
418
 
420
419
  return handle_browser_get_elements_by_text
@@ -422,7 +421,7 @@ def get_browser_get_elements_by_text_tool_handler(
422
421
 
423
422
  def get_browser_capture_screenshot_tool_definition(provider="claude") -> Dict[str, Any]:
424
423
  """Get tool definition for browser screenshot capture."""
425
- tool_description = "Capture page screenshot as base64 image data. Supports different formats and full page capture."
424
+ tool_description = "Capture page screenshot as base64 image data with colored boxes and UUID labels drawn over all identified elements. Supports different formats and full page capture."
426
425
  tool_arguments = {
427
426
  "format": {
428
427
  "type": "string",
@@ -500,7 +499,7 @@ def get_browser_capture_screenshot_tool_handler(
500
499
  return [screenshot_data]
501
500
  else:
502
501
  raise RuntimeError(
503
- f"Screenshot capture failed: {result.get('error', 'Unknown error')}"
502
+ f"Screenshot capture failed: {result.get('error', 'Unknown error')}"
504
503
  )
505
504
 
506
505
  return handle_browser_capture_screenshot
@@ -617,13 +616,13 @@ def get_browser_send_key_tool_handler(
617
616
  else ""
618
617
  )
619
618
  diff_summary = _get_content_delta_changes(browser_service)
620
- success_msg = f"{result.get('message', 'Success')}. {key_info}\nContent delta changes:\n{diff_summary}"
619
+ success_msg = f"{result.get('message', 'Success')}. {key_info}\nContent delta changes:\n{diff_summary}"
621
620
  if modifiers_info:
622
621
  success_msg += f". {modifiers_info}"
623
622
  return success_msg
624
623
  else:
625
624
  raise RuntimeError(
626
- f"Key send failed: {result.get('error', 'Unknown error')}"
625
+ f"Key send failed: {result.get('error', 'Unknown error')}"
627
626
  )
628
627
 
629
628
  return handle_browser_send_key
@@ -112,14 +112,6 @@ class ConversationManager:
112
112
  msg["tool_call_id"] = tool_result.get("tool_use_id", "")
113
113
 
114
114
  self.message_handler.current_conversation_id = conversation_id
115
- if self.message_handler.memory_service:
116
- self.message_handler.memory_service.session_id = (
117
- self.message_handler.current_conversation_id
118
- )
119
- self.message_handler.memory_service.loaded_conversation = True
120
- self.message_handler.memory_service.load_conversation_context(
121
- self.message_handler.current_conversation_id
122
- )
123
115
  last_agent_name = history[-1].get("agent", "")
124
116
  if last_agent_name and self.message_handler.agent_manager.select_agent(
125
117
  last_agent_name
@@ -129,6 +121,15 @@ class ConversationManager:
129
121
  )
130
122
  self.message_handler._notify("agent_changed", last_agent_name)
131
123
 
124
+ if self.message_handler.memory_service:
125
+ self.message_handler.memory_service.session_id = (
126
+ self.message_handler.current_conversation_id
127
+ )
128
+ self.message_handler.memory_service.loaded_conversation = True
129
+ self.message_handler.memory_service.load_conversation_context(
130
+ self.message_handler.current_conversation_id, last_agent_name
131
+ )
132
+
132
133
  self.message_handler.streamline_messages = history
133
134
  self.message_handler.agent_manager.rebuild_agents_messages(
134
135
  self.message_handler.streamline_messages
@@ -218,6 +218,7 @@ class InputHandler:
218
218
 
219
219
  @kb.add(Keys.ControlUp)
220
220
  @kb.add(Keys.Escape, Keys.Up)
221
+ @kb.add(Keys.ControlK)
221
222
  def _(event):
222
223
  """Navigate to previous history entry."""
223
224
  buffer = event.current_buffer
@@ -241,6 +242,7 @@ class InputHandler:
241
242
 
242
243
  @kb.add(Keys.ControlDown)
243
244
  @kb.add(Keys.Escape, Keys.Down)
245
+ @kb.add(Keys.ControlJ)
244
246
  def _(event):
245
247
  """Navigate to next history entry if cursor is at last line."""
246
248
  buffer = event.current_buffer
@@ -107,8 +107,7 @@ class UIEffects:
107
107
  self.live = Live(
108
108
  live_panel,
109
109
  console=self.console,
110
- auto_refresh=True,
111
- refresh_per_second=8,
110
+ auto_refresh=False,
112
111
  vertical_overflow="crop",
113
112
  )
114
113
  self.live.start()
@@ -167,10 +166,10 @@ class UIEffects:
167
166
  subtitle=subtitle,
168
167
  title_align="left",
169
168
  expand=False,
170
- height=height_limit if len(lines) >= height_limit - 10 else None,
169
+ height=min(height_limit, len(lines)),
171
170
  border_style=RICH_STYLE_GREEN,
172
171
  )
173
- self.live.update(live_panel)
172
+ self.live.update(live_panel, refresh=True)
174
173
 
175
174
  def finish_live_update(self):
176
175
  """Stop the live update display."""
@@ -121,16 +121,38 @@ class CustomLLMService(OpenAIService):
121
121
  # "max_tokens": 16000,
122
122
  }
123
123
  stream_params["temperature"] = self.temperature
124
- stream_params["extra_body"] = {"min_p": 0.1}
124
+ stream_params["extra_body"] = {"min_p": 0.02}
125
125
 
126
+ full_model_id = f"{self._provider_name}/{self.model}"
127
+
128
+ forced_sample_params = ModelRegistry.get_model_sample_params(full_model_id)
129
+ if forced_sample_params:
130
+ if forced_sample_params.temperature is not None:
131
+ stream_params["temperature"] = forced_sample_params.temperature
132
+ if forced_sample_params.top_p is not None:
133
+ stream_params["top_p"] = forced_sample_params.top_p
134
+ if forced_sample_params.top_k is not None:
135
+ stream_params["extra_body"]["top_k"] = forced_sample_params.top_k
136
+ if forced_sample_params.frequency_penalty is not None:
137
+ stream_params["frequency_penalty"] = (
138
+ forced_sample_params.frequency_penalty
139
+ )
140
+ if forced_sample_params.presence_penalty is not None:
141
+ stream_params["presence_penalty"] = (
142
+ forced_sample_params.presence_penalty
143
+ )
144
+ if forced_sample_params.repetition_penalty is not None:
145
+ stream_params["extra_body"]["repetition_penalty"] = (
146
+ forced_sample_params.repetition_penalty
147
+ )
148
+ if forced_sample_params.min_p is not None:
149
+ stream_params["extra_body"]["min_p"] = forced_sample_params.min_p
126
150
  # Add system message if provided
127
151
  if self.system_prompt:
128
152
  stream_params["messages"] = self._convert_internal_format(
129
153
  [{"role": "system", "content": self.system_prompt}] + messages
130
154
  )
131
155
 
132
- full_model_id = f"{self._provider_name}/{self.model}"
133
-
134
156
  # Add tools if available
135
157
  if self.tools and "tool_use" in ModelRegistry.get_model_capabilities(
136
158
  full_model_id
@@ -115,13 +115,13 @@ def get_file_write_or_edit_tool_handler(
115
115
  text_or_search_replace_blocks = params.get("text_or_search_replace_blocks")
116
116
 
117
117
  if not file_path:
118
- raise ValueError("Error: No file path provided.")
118
+ raise ValueError("Error: No file path provided.")
119
119
 
120
120
  if percentage_to_change is None:
121
- raise ValueError("Error: No percentage_to_change provided.")
121
+ raise ValueError("Error: No percentage_to_change provided.")
122
122
 
123
123
  if not text_or_search_replace_blocks:
124
- raise ValueError("Error: No content or search/replace blocks provided.")
124
+ raise ValueError("Error: No content or search/replace blocks provided.")
125
125
 
126
126
  result = file_editing_service.write_or_edit_file(
127
127
  file_path=file_path,
@@ -130,7 +130,7 @@ def get_file_write_or_edit_tool_handler(
130
130
  )
131
131
 
132
132
  if result["status"] == "success":
133
- parts = [f"{result['file_path']}"]
133
+ parts = [f"{result['file_path']}"]
134
134
  parts.append(f"{result.get('changes_applied', 1)} change(s)")
135
135
  if result.get("syntax_check", {}).get("is_valid"):
136
136
  parts.append(
@@ -153,21 +153,19 @@ def get_file_write_or_edit_tool_handler(
153
153
  else ""
154
154
  )
155
155
  restore = " | Backup restored" if result.get("backup_restored") else ""
156
- return (
157
- f"❌ Syntax ({result.get('language', '?')}):\n{errors}{extra}{restore}"
158
- )
156
+ return f"Syntax ({result.get('language', '?')}):\n{errors}{extra}{restore}"
159
157
 
160
158
  elif result["status"] in ["no_match", "ambiguous"]:
161
- return f"{result['status'].title()}: {result.get('error', '?')} (block {result.get('block_index', '?')})"
159
+ return f"{result['status'].title()}: {result.get('error', '?')} (block {result.get('block_index', '?')})"
162
160
 
163
161
  elif result["status"] == "denied":
164
- return f"Access denied: {result.get('error', 'Permission error')}"
162
+ return f"Access denied: {result.get('error', 'Permission error')}"
165
163
 
166
164
  elif result["status"] == "parse_error":
167
- return f"Parse: {result.get('error', 'Invalid block format')}"
165
+ return f"Parse: {result.get('error', 'Invalid block format')}"
168
166
 
169
167
  else:
170
- return f"{result.get('error', 'Unknown error')}"
168
+ return f"{result.get('error', 'Unknown error')}"
171
169
 
172
170
  return handle_file_write_or_edit
173
171
 
@@ -393,6 +393,19 @@ class GoogleAINativeService(BaseLLMService):
393
393
  top_p=0.95,
394
394
  )
395
395
 
396
+ forced_sample_params = ModelRegistry.get_model_sample_params(full_model_id)
397
+ if forced_sample_params:
398
+ if forced_sample_params.temperature is not None:
399
+ config.temperature = forced_sample_params.temperature
400
+ if forced_sample_params.top_p is not None:
401
+ config.top_p = forced_sample_params.top_p
402
+ if forced_sample_params.top_k is not None:
403
+ config.top_k = forced_sample_params.top_k
404
+ if forced_sample_params.frequency_penalty is not None:
405
+ config.frequency_penalty = forced_sample_params.frequency_penalty
406
+ if forced_sample_params.presence_penalty is not None:
407
+ config.presence_penalty = forced_sample_params.presence_penalty
408
+
396
409
  # Add system instruction if available
397
410
  if self.system_prompt:
398
411
  config.system_instruction = self.system_prompt
@@ -1,4 +1,4 @@
1
- from .types import Model
1
+ from .types import Model, SampleParam
2
2
 
3
3
  _ANTHROPIC_MODELS = [
4
4
  Model(
@@ -227,6 +227,17 @@ _GOOGLE_MODELS = [
227
227
  input_token_price_1m=1.25,
228
228
  output_token_price_1m=10,
229
229
  ),
230
+ Model(
231
+ id="gemini-3-pro-preview",
232
+ provider="google",
233
+ name="Gemini 3 Pro",
234
+ max_context_token=1_000_000,
235
+ description="Google's most intelligent model family to date, built on a foundation of state-of-the-art reasoning",
236
+ capabilities=["tool_use", "thinking", "vision", "structured_output"],
237
+ force_sample_params=SampleParam(temperature=1.0),
238
+ input_token_price_1m=2,
239
+ output_token_price_1m=12,
240
+ ),
230
241
  ]
231
242
 
232
243
  _DEEPINFRA_MODELS = [
@@ -254,6 +265,9 @@ _DEEPINFRA_MODELS = [
254
265
  name="Qwen 3 Coder",
255
266
  description="Qwen3-Coder-480B-A35B-Instruct is the Qwen3's most agentic code model",
256
267
  capabilities=["tool_use", "stream", "structured_output"],
268
+ force_sample_params=SampleParam(
269
+ temperature=0.7, top_p=0.8, top_k=20, repetition_penalty=1.05
270
+ ),
257
271
  input_token_price_1m=0.4,
258
272
  output_token_price_1m=1.6,
259
273
  ),
@@ -263,6 +277,9 @@ _DEEPINFRA_MODELS = [
263
277
  name="Qwen 3 Coder",
264
278
  description="Qwen3-Coder-480B-A35B-Instruct is the Qwen3's most agentic code model",
265
279
  capabilities=["tool_use", "stream", "structured_output"],
280
+ force_sample_params=SampleParam(
281
+ temperature=0.7, top_p=0.8, top_k=20, min_p=0.0
282
+ ),
266
283
  input_token_price_1m=0.14,
267
284
  output_token_price_1m=1.1,
268
285
  ),
@@ -272,6 +289,9 @@ _DEEPINFRA_MODELS = [
272
289
  name="Qwen 3 MoE 235B-22B",
273
290
  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",
274
291
  capabilities=["tool_use", "thinking", "stream", "structured_output"],
292
+ force_sample_params=SampleParam(
293
+ temperature=0.6, top_p=0.95, top_k=20, min_p=0.0
294
+ ),
275
295
  input_token_price_1m=0.2,
276
296
  output_token_price_1m=0.6,
277
297
  ),
@@ -280,6 +300,7 @@ _DEEPINFRA_MODELS = [
280
300
  provider="deepinfra",
281
301
  name="Zai GLM-4.6",
282
302
  description="The GLM-4.6 series models are foundation models designed for intelligent agents",
303
+ force_sample_params=SampleParam(temperature=1, top_p=0.95, top_k=40),
283
304
  capabilities=["tool_use", "stream", "structured_output"],
284
305
  input_token_price_1m=0.6,
285
306
  output_token_price_1m=2.0,
@@ -290,6 +311,9 @@ _DEEPINFRA_MODELS = [
290
311
  name="Qwen 3 32B",
291
312
  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",
292
313
  capabilities=["tool_use", "stream", "structured_output"],
314
+ force_sample_params=SampleParam(
315
+ temperature=0.6, top_p=0.95, top_k=20, min_p=0.0
316
+ ),
293
317
  input_token_price_1m=0.1,
294
318
  output_token_price_1m=0.3,
295
319
  ),
@@ -308,6 +332,7 @@ _DEEPINFRA_MODELS = [
308
332
  name="DeepSeek R1 0528",
309
333
  description="The DeepSeek R1 model has undergone a minor version upgrade, with the current version being DeepSeek-R1-0528.",
310
334
  capabilities=["tool_use", "thinking", "stream", "structured_output"],
335
+ force_sample_params=SampleParam(temperature=0.6),
311
336
  input_token_price_1m=0.5,
312
337
  output_token_price_1m=2.18,
313
338
  ),
@@ -317,6 +342,7 @@ _DEEPINFRA_MODELS = [
317
342
  name="Kimi K2 Instruct",
318
343
  description="Kimi K2 is a large-scale Mixture-of-Experts (MoE) language model developed by Moonshot AI, featuring 1 trillion total parameters with 32 billion active per forward pass",
319
344
  capabilities=["tool_use", "stream", "structured_output"],
345
+ force_sample_params=SampleParam(temperature=0.6),
320
346
  input_token_price_1m=0.5,
321
347
  output_token_price_1m=2.0,
322
348
  ),
@@ -342,6 +368,17 @@ _GITHUB_COPILOT_MODELS = [
342
368
  input_token_price_1m=0.0,
343
369
  output_token_price_1m=0.0,
344
370
  ),
371
+ Model(
372
+ id="gemini-3-pro-preview",
373
+ provider="github_copilot",
374
+ name="Gemini 3 Pro",
375
+ description="",
376
+ capabilities=["tool_use", "vision", "stream"],
377
+ default=False,
378
+ input_token_price_1m=0.0,
379
+ force_sample_params=SampleParam(temperature=1.0),
380
+ output_token_price_1m=0.0,
381
+ ),
345
382
  Model(
346
383
  id="gpt-4.1",
347
384
  provider="github_copilot",
@@ -46,6 +46,15 @@ class ModelRegistry:
46
46
  return 128_000
47
47
  return model.max_context_token
48
48
 
49
+ @classmethod
50
+ def get_model_sample_params(cls, mode_id):
51
+ registry = ModelRegistry.get_instance()
52
+ model = registry.get_model(mode_id)
53
+ if not model or not model.force_sample_params:
54
+ logger.warning(f"Model not found in registry: {mode_id}")
55
+ return None
56
+ return model.force_sample_params
57
+
49
58
  def _load_custom_models_from_config(self):
50
59
  """Loads models from custom LLM provider configurations and registers them."""
51
60
  try:
@@ -1,5 +1,15 @@
1
1
  from pydantic import BaseModel
2
- from typing import List, Literal
2
+ from typing import List, Literal, Optional
3
+
4
+
5
+ class SampleParam(BaseModel):
6
+ temperature: Optional[float] = None
7
+ top_p: Optional[float] = None
8
+ min_p: Optional[float] = None
9
+ top_k: Optional[int] = None
10
+ frequency_penalty: Optional[float] = None
11
+ presence_penalty: Optional[float] = None
12
+ repetition_penalty: Optional[float] = None
3
13
 
4
14
 
5
15
  class Model(BaseModel):
@@ -19,6 +29,7 @@ class Model(BaseModel):
19
29
  ]
20
30
  ]
21
31
  default: bool = False
32
+ force_sample_params: Optional[SampleParam] = None
22
33
  max_context_token: int = 128_000
23
34
  input_token_price_1m: float = 0.0
24
35
  output_token_price_1m: float = 0.0
@@ -50,7 +50,7 @@ class BaseMemoryService(ABC):
50
50
  pass
51
51
 
52
52
  @abstractmethod
53
- def load_conversation_context(self, session_id: str):
53
+ def load_conversation_context(self, session_id: str, agent_name: str = "None"):
54
54
  pass
55
55
 
56
56
  @abstractmethod
@@ -88,7 +88,7 @@ class BaseMemoryService(ABC):
88
88
  pass
89
89
 
90
90
  @abstractmethod
91
- def list_memory_ids(
91
+ def list_memory_headers(
92
92
  self,
93
93
  from_date: Optional[int] = None,
94
94
  to_date: Optional[int] = None,