jarvis-ai-assistant 0.4.1__py3-none-any.whl → 0.4.2__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.
jarvis/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.4.1"
4
+ __version__ = "0.4.2"
@@ -9,6 +9,7 @@ from pathlib import Path
9
9
  from enum import Enum
10
10
  from typing import Any, Callable, Dict, List, Optional, Protocol, Tuple, Union
11
11
 
12
+
12
13
  # 第三方库导入
13
14
  from rich.align import Align
14
15
  from rich.console import Console
@@ -35,7 +36,6 @@ from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
35
36
  from jarvis.jarvis_agent.rewrite_file_handler import RewriteFileHandler
36
37
  from jarvis.jarvis_agent.prompt_manager import PromptManager
37
38
  from jarvis.jarvis_agent.event_bus import EventBus
38
- from jarvis.jarvis_agent.config import AgentConfig
39
39
  from jarvis.jarvis_agent.run_loop import AgentRunLoop
40
40
  from jarvis.jarvis_agent.events import (
41
41
  BEFORE_SUMMARY,
@@ -67,7 +67,6 @@ from jarvis.jarvis_platform.registry import PlatformRegistry
67
67
  # jarvis_utils 相关
68
68
  from jarvis.jarvis_utils.config import (
69
69
  get_data_dir,
70
- get_max_token_count,
71
70
  get_normal_model_name,
72
71
  get_normal_platform_name,
73
72
  is_execute_tool_confirm,
@@ -76,7 +75,8 @@ from jarvis.jarvis_utils.config import (
76
75
  is_use_methodology,
77
76
  get_tool_filter_threshold,
78
77
  get_after_tool_call_cb_dirs,
79
- set_config,
78
+
79
+
80
80
  )
81
81
  from jarvis.jarvis_utils.embedding import get_context_token_count
82
82
  from jarvis.jarvis_utils.globals import (
@@ -282,6 +282,7 @@ class Agent:
282
282
  use_tools: Optional[List[str]] = None,
283
283
  execute_tool_confirm: Optional[bool] = None,
284
284
  need_summary: bool = True,
285
+ auto_summary_rounds: Optional[int] = None,
285
286
  multiline_inputer: Optional[Callable[[str], str]] = None,
286
287
  use_methodology: Optional[bool] = None,
287
288
  use_analysis: Optional[bool] = None,
@@ -289,6 +290,7 @@ class Agent:
289
290
  files: Optional[List[str]] = None,
290
291
  confirm_callback: Optional[Callable[[str, bool], bool]] = None,
291
292
  non_interactive: Optional[bool] = None,
293
+ in_multi_agent: Optional[bool] = None,
292
294
  **kwargs,
293
295
  ):
294
296
  """初始化Jarvis Agent实例
@@ -309,20 +311,36 @@ class Agent:
309
311
  confirm_callback: 用户确认回调函数,签名为 (tip: str, default: bool) -> bool;默认使用CLI的user_confirm
310
312
  non_interactive: 是否以非交互模式运行(优先级最高,覆盖环境变量与配置)
311
313
  """
312
- # 基础属性初始化
313
- self.files = files or []
314
+ # 基础属性初始化(仅根据入参设置原始值;实际生效的默认回退在 _init_config 中统一解析)
315
+ # 标识与描述
314
316
  self.name = make_agent_name(name)
315
317
  self.description = description
316
318
  self.system_prompt = system_prompt
317
- self.need_summary = need_summary
318
- self.auto_complete = auto_complete
319
+ # 行为控制开关(原始入参值)
320
+ self.auto_complete = bool(auto_complete)
321
+ self.need_summary = bool(need_summary)
322
+ # 自动摘要轮次:None 表示使用配置文件中的默认值,由 AgentRunLoop 决定最终取值
323
+ self.auto_summary_rounds = auto_summary_rounds
324
+ self.use_methodology = use_methodology
325
+ self.use_analysis = use_analysis
326
+ self.execute_tool_confirm = execute_tool_confirm
327
+ self.summary_prompt = summary_prompt
328
+ self.force_save_memory = force_save_memory
329
+ # 资源与环境
330
+ self.model_group = model_group
331
+ self.files = files or []
332
+ self.use_tools = use_tools
333
+ self.non_interactive = non_interactive
334
+ # 多智能体运行标志:用于控制非交互模式下的自动完成行为
335
+ self.in_multi_agent = bool(in_multi_agent)
336
+ # 运行时状态
319
337
  self.first = True
320
338
  self.run_input_handlers_next_turn = False
321
339
  self.user_data: Dict[str, Any] = {}
322
340
 
323
341
 
324
342
  # 用户确认回调:默认使用 CLI 的 user_confirm,可由外部注入以支持 TUI/GUI
325
- self.user_confirm: Callable[[str, bool], bool] = (
343
+ self.confirm_callback: Callable[[str, bool], bool] = (
326
344
  confirm_callback or user_confirm # type: ignore[assignment]
327
345
  )
328
346
 
@@ -337,9 +355,9 @@ class Agent:
337
355
  use_tools or [],
338
356
  )
339
357
  # 初始化用户交互封装,保持向后兼容
340
- self.user_interaction = UserInteractionHandler(self.multiline_inputer, self.user_confirm)
358
+ self.user_interaction = UserInteractionHandler(self.multiline_inputer, self.confirm_callback)
341
359
  # 将确认函数指向封装后的 confirm,保持既有调用不变
342
- self.user_confirm = self.user_interaction.confirm # type: ignore[assignment]
360
+ self.confirm_callback = self.user_interaction.confirm # type: ignore[assignment]
343
361
  # 非交互模式参数支持:允许通过构造参数显式控制,便于其他Agent调用时设置
344
362
  try:
345
363
  # 优先使用构造参数,其次回退到环境变量
@@ -351,24 +369,43 @@ class Agent:
351
369
  # 如果构造参数显式提供,则同步到环境变量与全局配置,供下游组件读取
352
370
  if non_interactive is not None:
353
371
  os.environ["JARVIS_NON_INTERACTIVE"] = "true" if self.non_interactive else "false"
354
- try:
355
- set_config("JARVIS_NON_INTERACTIVE", self.non_interactive)
356
- except Exception:
357
- # 配置同步失败不影响主流程
358
- pass
372
+
359
373
  except Exception:
360
374
  # 防御式回退
361
375
  self.non_interactive = False
362
376
 
363
- # 初始化配置
364
- self._init_config(
365
- use_methodology,
366
- use_analysis,
367
- execute_tool_confirm,
368
- summary_prompt,
369
- model_group,
370
- force_save_memory,
371
- )
377
+ # 初始化配置(直接解析,不再依赖 _init_config)
378
+ try:
379
+ resolved_use_methodology = bool(use_methodology if use_methodology is not None else is_use_methodology())
380
+ except Exception:
381
+ resolved_use_methodology = bool(use_methodology) if use_methodology is not None else True
382
+
383
+ try:
384
+ resolved_use_analysis = bool(use_analysis if use_analysis is not None else is_use_analysis())
385
+ except Exception:
386
+ resolved_use_analysis = bool(use_analysis) if use_analysis is not None else True
387
+
388
+ try:
389
+ resolved_execute_tool_confirm = bool(execute_tool_confirm if execute_tool_confirm is not None else is_execute_tool_confirm())
390
+ except Exception:
391
+ resolved_execute_tool_confirm = bool(execute_tool_confirm) if execute_tool_confirm is not None else False
392
+
393
+ try:
394
+ resolved_force_save_memory = bool(force_save_memory if force_save_memory is not None else is_force_save_memory())
395
+ except Exception:
396
+ resolved_force_save_memory = bool(force_save_memory) if force_save_memory is not None else False
397
+
398
+ self.use_methodology = resolved_use_methodology
399
+ self.use_analysis = resolved_use_analysis
400
+ self.execute_tool_confirm = resolved_execute_tool_confirm
401
+ self.summary_prompt = (summary_prompt or DEFAULT_SUMMARY_PROMPT)
402
+ self.force_save_memory = resolved_force_save_memory
403
+ # 多智能体模式下,默认不自动完成(即使是非交互),仅在明确传入 auto_complete=True 时开启
404
+ if self.in_multi_agent:
405
+ self.auto_complete = bool(self.auto_complete)
406
+ else:
407
+ # 非交互模式下默认自动完成;否则保持传入的 auto_complete 值
408
+ self.auto_complete = bool(self.auto_complete or (self.non_interactive or False))
372
409
 
373
410
  # 初始化事件总线需先于管理器,以便管理器在构造中安全订阅事件
374
411
  self.event_bus = EventBus()
@@ -432,46 +469,6 @@ class Agent:
432
469
  ]
433
470
  self.multiline_inputer = multiline_inputer or get_multiline_input
434
471
 
435
- def _init_config(
436
- self,
437
- use_methodology: Optional[bool],
438
- use_analysis: Optional[bool],
439
- execute_tool_confirm: Optional[bool],
440
- summary_prompt: Optional[str],
441
- model_group: Optional[str],
442
- force_save_memory: Optional[bool],
443
- ):
444
- """初始化配置选项"""
445
- # 使用集中配置解析,保持与原逻辑一致
446
- cfg = AgentConfig(
447
- system_prompt=self.system_prompt,
448
- name=self.name,
449
- description=self.description,
450
- model_group=model_group,
451
- auto_complete=self.auto_complete,
452
- need_summary=self.need_summary,
453
- summary_prompt=summary_prompt,
454
- execute_tool_confirm=execute_tool_confirm,
455
- use_methodology=use_methodology,
456
- use_analysis=use_analysis,
457
- force_save_memory=force_save_memory,
458
- files=self.files,
459
- max_token_count=None,
460
- ).resolve_defaults()
461
-
462
- # 将解析结果回填到 Agent 实例属性,保持向后兼容
463
- self.use_methodology = bool(cfg.use_methodology)
464
- self.use_analysis = bool(cfg.use_analysis)
465
- self.execute_tool_confirm = bool(cfg.execute_tool_confirm)
466
- self.summary_prompt = cfg.summary_prompt or DEFAULT_SUMMARY_PROMPT
467
- self.max_token_count = int(cfg.max_token_count or get_max_token_count(model_group))
468
- self.force_save_memory = bool(cfg.force_save_memory)
469
- # 非交互模式下自动完成标志需要同步到 Agent 实例,避免循环
470
- self.auto_complete = bool(cfg.auto_complete)
471
-
472
- # 聚合配置到 AgentConfig,作为后续单一事实来源(保持兼容,不改变既有属性使用)
473
- self.config = cfg
474
-
475
472
  def _setup_system_prompt(self):
476
473
  """设置系统提示词"""
477
474
  try:
@@ -731,14 +728,9 @@ class Agent:
731
728
  return message
732
729
 
733
730
  def _manage_conversation_length(self, message: str) -> str:
734
- """管理对话长度,必要时进行摘要"""
731
+ """管理对话长度计数;摘要触发由轮次在 AgentRunLoop 中统一处理。"""
735
732
  self.session.conversation_length += get_context_token_count(message)
736
733
 
737
- if self.session.conversation_length > self.max_token_count:
738
- summary = self._summarize_and_clear_history()
739
- if summary:
740
- message = join_prompts([summary, message])
741
- self.session.conversation_length = get_context_token_count(message)
742
734
 
743
735
  return message
744
736
 
@@ -1095,7 +1087,7 @@ class Agent:
1095
1087
  return self._complete_task(auto_completed=False)
1096
1088
 
1097
1089
  if any(handler.can_handle(current_response) for handler in self.output_handler):
1098
- if self.user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
1090
+ if self.confirm_callback("检测到有工具调用,是否继续处理工具调用?", True):
1099
1091
  self.session.prompt = join_prompts([
1100
1092
  f"被用户中断,用户补充信息为:{user_input}",
1101
1093
  "用户同意继续工具调用。"
@@ -1322,7 +1314,7 @@ class Agent:
1322
1314
  f"并且存在3个以上标签重叠的记忆。\n"
1323
1315
  f"是否立即整理记忆库以优化性能和相关性?"
1324
1316
  )
1325
- if self.user_confirm(prompt, True):
1317
+ if self.confirm_callback(prompt, True):
1326
1318
  PrettyOutput.print(
1327
1319
  f"正在开始整理 '{scope_name}' ({memory_type}) 记忆库...",
1328
1320
  OutputType.INFO,
@@ -14,6 +14,7 @@ from typing import Any, TYPE_CHECKING
14
14
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
15
15
  from jarvis.jarvis_agent.events import BEFORE_TOOL_CALL, AFTER_TOOL_CALL
16
16
  from jarvis.jarvis_agent.utils import join_prompts, is_auto_complete, normalize_next_action
17
+ from jarvis.jarvis_utils.config import get_auto_summary_rounds
17
18
 
18
19
  if TYPE_CHECKING:
19
20
  # 仅用于类型标注,避免运行时循环依赖
@@ -25,6 +26,12 @@ class AgentRunLoop:
25
26
  self.agent = agent
26
27
  self.conversation_rounds = 0
27
28
  self.tool_reminder_rounds = int(os.environ.get("JARVIS_TOOL_REMINDER_ROUNDS", 20))
29
+ # 基于轮次的自动总结阈值:优先使用 Agent 入参,否则回落到全局配置(默认20轮)
30
+ self.auto_summary_rounds = (
31
+ self.agent.auto_summary_rounds
32
+ if getattr(self.agent, "auto_summary_rounds", None) is not None
33
+ else get_auto_summary_rounds()
34
+ )
28
35
 
29
36
  def run(self) -> Any:
30
37
  """主运行循环(委派到传入的 agent 实例的方法与属性)"""
@@ -37,6 +44,17 @@ class AgentRunLoop:
37
44
  self.agent.session.addon_prompt = join_prompts(
38
45
  [self.agent.session.addon_prompt, self.agent.get_tool_usage_prompt()]
39
46
  )
47
+ # 基于轮次的自动总结判断:达到阈值后执行一次总结与历史清理
48
+ if self.conversation_rounds >= self.auto_summary_rounds:
49
+ summary_text = self.agent._summarize_and_clear_history()
50
+ if summary_text:
51
+ # 将摘要作为下一轮的附加提示加入,从而维持上下文连续性
52
+ self.agent.session.addon_prompt = join_prompts(
53
+ [self.agent.session.addon_prompt, summary_text]
54
+ )
55
+ # 重置轮次计数与对话长度计数器,开始新一轮周期
56
+ self.conversation_rounds = 0
57
+ self.agent.session.conversation_length = 0
40
58
 
41
59
  ag = self.agent
42
60
 
@@ -193,6 +193,11 @@
193
193
  "description": "脚本执行的超时时间(秒),仅在非交互模式下生效。",
194
194
  "default": 300
195
195
  },
196
+ "JARVIS_AUTO_SUMMARY_ROUNDS": {
197
+ "type": "number",
198
+ "description": "基于对话轮次的自动总结阈值(达到该轮次后自动总结并清理历史)",
199
+ "default": 20
200
+ },
196
201
  "JARVIS_CONFIRM_BEFORE_APPLY_PATCH": {
197
202
  "type": "boolean",
198
203
  "description": "应用补丁前是否需要确认",
@@ -287,6 +287,11 @@ content: |2
287
287
  return None
288
288
 
289
289
  config = self.agents_config_map[name].copy()
290
+ # 标记为多智能体运行,避免在非交互模式下自动开启 auto_complete
291
+ config.setdefault("in_multi_agent", True)
292
+ # 非主智能体统一禁用自动补全,防止多智能体并行时误触发自动交互
293
+ if name != self.main_agent_name:
294
+ config["auto_complete"] = False
290
295
 
291
296
  # Prepend common system prompt if configured
292
297
  common_sp = getattr(self, "common_system_prompt", "")
@@ -328,9 +333,14 @@ content: |2
328
333
  break
329
334
 
330
335
  # Generate a brief summary via direct model call to avoid run-loop recursion
331
- try:
332
- # 参照 Agent.generate_summary 的实现思路:基于当前 session.prompt 追加请求提示,直接调用底层模型
333
- multi_agent_summary_prompt = """
336
+ # 如果在配置中显式设置了 summary_on_send=False,则不生成摘要
337
+ sender_config = self.agents_config_map.get(last_agent_name, {}) if hasattr(self, "agents_config_map") else {}
338
+ summary_on_send = sender_config.get("summary_on_send", True)
339
+ summary_text = ""
340
+ if summary_on_send:
341
+ try:
342
+ # 参照 Agent.generate_summary 的实现思路:基于当前 session.prompt 追加请求提示,直接调用底层模型
343
+ multi_agent_summary_prompt = """
334
344
  请基于当前会话,为即将发送给其他智能体的协作交接写一段摘要,包含:
335
345
  - 已完成的主要工作与产出
336
346
  - 关键决策及其理由
@@ -341,12 +351,12 @@ content: |2
341
351
  - 仅输出纯文本,不包含任何指令或工具调用
342
352
  - 使用简洁的要点式表述
343
353
  """.strip()
344
- summary_any: Any = agent.model.chat_until_success( # type: ignore[attr-defined]
345
- f"{agent.session.prompt}\n{multi_agent_summary_prompt}"
346
- )
347
- summary_text = summary_any.strip() if isinstance(summary_any, str) else ""
348
- except Exception:
349
- summary_text = ""
354
+ summary_any: Any = agent.model.chat_until_success( # type: ignore[attr-defined]
355
+ f"{agent.session.prompt}\n{multi_agent_summary_prompt}"
356
+ )
357
+ summary_text = summary_any.strip() if isinstance(summary_any, str) else ""
358
+ except Exception:
359
+ summary_text = ""
350
360
  prompt = f"""
351
361
  Please handle this message:
352
362
  from: {last_agent_name}
@@ -373,6 +383,9 @@ content: {msg['content']}
373
383
  f"{last_agent_name} 正在向 {to_agent_name} 发送消息...", OutputType.INFO
374
384
  )
375
385
 
386
+ # Keep a reference to the sender before switching to the receiver
387
+ sender_agent = agent
388
+
376
389
  agent = self._get_agent(to_agent_name)
377
390
  if not agent:
378
391
  return f"智能体 {to_agent_name} 未找到"
@@ -380,9 +393,9 @@ content: {msg['content']}
380
393
  # Check if the sending agent should be cleared
381
394
  sender_config = self.agents_config_map.get(last_agent_name, {})
382
395
  if sender_config.get("clear_after_send_message"):
383
- if agent:
396
+ if sender_agent:
384
397
  PrettyOutput.print(f"清除智能体 {last_agent_name} 在发送消息后的历史记录...", OutputType.INFO)
385
- agent.clear_history()
398
+ sender_agent.clear_history()
386
399
 
387
400
  last_agent_name = agent.name
388
401
  msg = agent.run(prompt)
@@ -633,6 +633,12 @@ class ToolRegistry(OutputHandlerProtocol):
633
633
  异常:
634
634
  Exception: 如果工具调用缺少必要字段
635
635
  """
636
+ # 如果</TOOL_CALL>出现在响应的末尾,但是前面没有换行符,自动插入一个换行符进行修复
637
+ if content.rstrip().endswith(ct("TOOL_CALL")):
638
+ pos = content.rfind(ct("TOOL_CALL"))
639
+ if pos > 0 and content[pos - 1] not in ("\n", "\r"):
640
+ content = content[:pos] + "\n" + content[pos:]
641
+
636
642
  # 将内容拆分为行
637
643
  pattern = rf'(?ms){re.escape(ot("TOOL_CALL"))}(.*?)^{re.escape(ct("TOOL_CALL"))}'
638
644
  data = re.findall(pattern, content)
@@ -51,6 +51,10 @@ class SubAgentTool:
51
51
  "type": "string",
52
52
  "description": "覆盖子Agent的总结提示词(必填)",
53
53
  },
54
+ "non_interactive": {
55
+ "type": "boolean",
56
+ "description": "是否启用无交互模式(可选,默认继承父Agent或系统默认)",
57
+ },
54
58
  },
55
59
  "required": [
56
60
  "task",
@@ -131,6 +135,11 @@ class SubAgentTool:
131
135
  # 安全兜底:无法从父Agent获取配置则保持为None,使用系统默认
132
136
  pass
133
137
 
138
+ # 可选参数:允许显式覆盖无交互模式
139
+ explicit_non_interactive = args.get("non_interactive", None)
140
+ if explicit_non_interactive is not None:
141
+ parent_non_interactive = bool(explicit_non_interactive)
142
+
134
143
  agent = Agent(
135
144
  system_prompt=system_prompt,
136
145
  name=agent_name,
@@ -76,17 +76,6 @@ def get_replace_map() -> dict:
76
76
  return {**BUILTIN_REPLACE_MAP, **file_map}
77
77
 
78
78
 
79
- def get_max_token_count(model_group_override: Optional[str] = None) -> int:
80
- """
81
- 获取模型允许的最大token数量。
82
-
83
- 返回:
84
- int: 模型能处理的最大token数量,为最大输入token数量的30倍。
85
- """
86
- max_input_tokens = get_max_input_token_count(model_group_override)
87
- return max_input_tokens * 30
88
-
89
-
90
79
  def get_max_input_token_count(model_group_override: Optional[str] = None) -> int:
91
80
  """
92
81
  获取模型允许的最大输入token数量。
@@ -712,6 +701,22 @@ def get_tool_filter_threshold() -> int:
712
701
  return int(GLOBAL_CONFIG_DATA.get("JARVIS_TOOL_FILTER_THRESHOLD", 30))
713
702
 
714
703
 
704
+ def get_auto_summary_rounds() -> int:
705
+ """
706
+ 获取基于对话轮次的自动总结阈值。
707
+
708
+ 返回:
709
+ int: 轮次阈值,默认20
710
+ """
711
+ try:
712
+ return int(GLOBAL_CONFIG_DATA.get("JARVIS_AUTO_SUMMARY_ROUNDS", 20))
713
+ except Exception:
714
+ return 20
715
+
716
+
717
+
718
+
719
+
715
720
  def get_script_execution_timeout() -> int:
716
721
  """
717
722
  获取脚本执行的超时时间(秒)。
@@ -363,11 +363,50 @@ class FileCompleter(Completer):
363
363
  return tag
364
364
 
365
365
 
366
- def user_confirm(tip: str, default: bool = True) -> bool:
367
- """提示用户确认是/否问题"""
366
+ # ---------------------
367
+ # 公共判定辅助函数(按当前Agent优先)
368
+ # ---------------------
369
+ def _get_current_agent_for_input():
370
+ try:
371
+ import jarvis.jarvis_utils.globals as g
372
+ current_name = getattr(g, "current_agent_name", "")
373
+ if current_name:
374
+ return g.get_agent(current_name)
375
+ except Exception:
376
+ pass
377
+ return None
378
+
379
+ def _is_non_interactive_for_current_agent() -> bool:
368
380
  try:
369
381
  from jarvis.jarvis_utils.config import is_non_interactive
370
- if is_non_interactive():
382
+ ag = _get_current_agent_for_input()
383
+ try:
384
+ return bool(getattr(ag, "non_interactive", False)) if ag else bool(is_non_interactive())
385
+ except Exception:
386
+ return bool(is_non_interactive())
387
+ except Exception:
388
+ return False
389
+
390
+ def _is_auto_complete_for_current_agent() -> bool:
391
+ try:
392
+ from jarvis.jarvis_utils.config import GLOBAL_CONFIG_DATA
393
+ ag = _get_current_agent_for_input()
394
+ if ag is not None and hasattr(ag, "auto_complete"):
395
+ try:
396
+ return bool(getattr(ag, "auto_complete", False))
397
+ except Exception:
398
+ pass
399
+ env_v = os.getenv("JARVIS_AUTO_COMPLETE")
400
+ if env_v is not None:
401
+ return str(env_v).strip().lower() in ("1", "true", "yes", "on")
402
+ return bool(GLOBAL_CONFIG_DATA.get("JARVIS_AUTO_COMPLETE", False))
403
+ except Exception:
404
+ return False
405
+
406
+ def user_confirm(tip: str, default: bool = True) -> bool:
407
+ """提示用户确认是/否问题(按当前Agent优先判断非交互)"""
408
+ try:
409
+ if _is_non_interactive_for_current_agent():
371
410
  return default
372
411
  suffix = "[Y/n]" if default else "[y/N]"
373
412
  ret = get_single_line_input(f"{tip} {suffix}: ")
@@ -666,9 +705,41 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
666
705
  preset: Optional[str] = None
667
706
  preset_cursor: Optional[int] = None
668
707
  while True:
669
- from jarvis.jarvis_utils.config import is_non_interactive
670
- if is_non_interactive():
671
- return "我无法与你交互,所有的事情你都自我决策,如果无法决策,就完成任务"
708
+ from jarvis.jarvis_utils.config import is_non_interactive, GLOBAL_CONFIG_DATA
709
+ # 基于“当前Agent”精确判断非交互与自动完成,避免多Agent相互干扰
710
+ if _is_non_interactive_for_current_agent():
711
+ # 在多Agent系统中,无论是否启用自动完成,均提示可用智能体并建议使用 SEND_MESSAGE 转移控制权
712
+ hint = ""
713
+ try:
714
+ ag = _get_current_agent_for_input()
715
+ ohs = getattr(ag, "output_handler", [])
716
+ available_agents: List[str] = []
717
+ for oh in (ohs or []):
718
+ cfgs = getattr(oh, "agents_config", None)
719
+ if isinstance(cfgs, list):
720
+ for c in cfgs:
721
+ try:
722
+ name = c.get("name")
723
+ except Exception:
724
+ name = None
725
+ if isinstance(name, str) and name.strip():
726
+ available_agents.append(name.strip())
727
+ if available_agents:
728
+ # 去重但保留顺序
729
+ seen = set()
730
+ ordered = []
731
+ for n in available_agents:
732
+ if n not in seen:
733
+ seen.add(n)
734
+ ordered.append(n)
735
+ hint = "\n当前可用智能体: " + ", ".join(ordered) + f"\n如需将任务交给其他智能体,请使用 {ot('SEND_MESSAGE')} 块。"
736
+ except Exception:
737
+ hint = ""
738
+ if _is_auto_complete_for_current_agent():
739
+ base_msg = "我无法与你交互,所有的事情你都自我决策,如果无法决策,就完成任务。输出" + ot("!!!COMPLETE!!!")
740
+ return base_msg + hint
741
+ else:
742
+ return "我无法与你交互,所有的事情你都自我决策" + hint
672
743
  user_input = _get_multiline_input_internal(
673
744
  tip, preset=preset, preset_cursor=preset_cursor
674
745
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: Jarvis: An AI assistant that uses tools to interact with the system
5
5
  Home-page: https://github.com/skyfireitdiy/Jarvis
6
6
  Author: skyfire
@@ -1,8 +1,7 @@
1
- jarvis/__init__.py,sha256=AruQMrAiWbon9fxEdGwHgikJfqVwLLpY-3aXhycGePc,73
2
- jarvis/jarvis_agent/__init__.py,sha256=NzMMY4H73aPvRAsK-1G7iNN8beEH1RCkkXK6A9NSAts,51029
1
+ jarvis/__init__.py,sha256=NIbT8I8sAw3cCWWXtArSe6_eWeB17Gf7WmiF9nyxdKs,73
2
+ jarvis/jarvis_agent/__init__.py,sha256=OOoHubIIQ3A9xB7jHFRpJKDtAk0or8Y7TmUkdq00l4Q,51386
3
3
  jarvis/jarvis_agent/agent_manager.py,sha256=Q0S-mYTPt8Xd7RKGoLWoWU_RP_wEXFWxCjve8_t2f2A,3807
4
4
  jarvis/jarvis_agent/builtin_input_handler.py,sha256=wS-FqpT3pIXwHn1dfL3SpXonUKWgVThbQueUIeyRc2U,2917
5
- jarvis/jarvis_agent/config.py,sha256=f_VmSfdEtl4a32y-fVxhcZYkni4RznBAUKjv6kET1LU,3517
6
5
  jarvis/jarvis_agent/config_editor.py,sha256=hlb9EYxKWcR_qdW2O89CgNDdciR9Isi743JU_1gD8j4,1927
7
6
  jarvis/jarvis_agent/edit_file_handler.py,sha256=8j7SRSWTw468fTuR-ueNKFK_0pR1GwDxTvsGGmm3zlA,24714
8
7
  jarvis/jarvis_agent/event_bus.py,sha256=pRdfk7d0OG18K6yNfWlCvAh_dW5p9sBtT2Yc3jGmzgo,1519
@@ -19,7 +18,7 @@ jarvis/jarvis_agent/prompt_manager.py,sha256=_1qLBSA3yn4nT_N3X2npTpW40Cp-pMeyvnz
19
18
  jarvis/jarvis_agent/prompts.py,sha256=CvbPYx_klEz6OQrxVReZAnC2uQNo53rWkkucmh30uKg,9531
20
19
  jarvis/jarvis_agent/protocols.py,sha256=YFJaC9MHi7JfLzmvlyotJDjiCO4Z07XJXy1gKhVdUy4,956
21
20
  jarvis/jarvis_agent/rewrite_file_handler.py,sha256=FVSrfrC115_cGvdPW9RIn3A-gQAhok7GyyBfnOFdpXs,5276
22
- jarvis/jarvis_agent/run_loop.py,sha256=kxVwFdzhzP7ZY5_aWl4-HvoQtGr4eYr8SytzvJDEDnU,5006
21
+ jarvis/jarvis_agent/run_loop.py,sha256=iGfa28J2K6I07k6p66O3WJFSk9z4uOarqe6CLqALIsk,6167
23
22
  jarvis/jarvis_agent/session_manager.py,sha256=5wVcaZGwJ9cEKTQglSbqyxUDJ2fI5KxYN8C8L16UWLw,3024
24
23
  jarvis/jarvis_agent/share_manager.py,sha256=MF2RlomcgPxF8nVUk28DP6IRddZ_tot5l_YRvy0qXSQ,8726
25
24
  jarvis/jarvis_agent/shell_input_handler.py,sha256=wiAPjB-9uTkcLszbO5dlOUwIfaeR39RgRcZhahIGqoA,2018
@@ -57,7 +56,7 @@ jarvis/jarvis_code_analysis/checklists/shell.py,sha256=aRFYhQQvTgbYd-uY5pc8UHIUA
57
56
  jarvis/jarvis_code_analysis/checklists/sql.py,sha256=vR0T6qC7b4dURjJVAd7kSVxyvZEQXPG1Jqc2sNTGp5c,2355
58
57
  jarvis/jarvis_code_analysis/checklists/swift.py,sha256=TPx4I6Gupvs6tSerRKmTSKEPQpOLEbH2Y7LXg1uBgxc,2566
59
58
  jarvis/jarvis_code_analysis/checklists/web.py,sha256=25gGD7pDadZQybNFvALYxWvK0VRjGQb1NVJQElwjyk0,3943
60
- jarvis/jarvis_data/config_schema.json,sha256=zfQni3t9KjfFd4gb6shJA0bqaVDSqBA72VMokMFQJ9c,14682
59
+ jarvis/jarvis_data/config_schema.json,sha256=VlPqakEXx7k2wm5Uv-ZlbKeIPbHNc98r1pHFtXEBBBY,14884
61
60
  jarvis/jarvis_data/tiktoken/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
62
61
  jarvis/jarvis_git_squash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
62
  jarvis/jarvis_git_squash/main.py,sha256=BRbsEQVXwseVFKliVqV8_JPh1om6QT6dLTHw0jQ7OE0,2474
@@ -69,7 +68,7 @@ jarvis/jarvis_mcp/streamable_mcp_client.py,sha256=BenOeZGNHdUOJT5Z3cc5MhS6aOeKQg
69
68
  jarvis/jarvis_memory_organizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
69
  jarvis/jarvis_memory_organizer/memory_organizer.py,sha256=CMFL46vvtpcTI6oS3CAlYteR6xAlwCkvVJmMT22uDRw,26295
71
70
  jarvis/jarvis_methodology/main.py,sha256=uiNzk5b5O6xdvRhsOuD7ubxdd2tPcDsFFnvmes8uH8I,11370
72
- jarvis/jarvis_multi_agent/__init__.py,sha256=Hr3AHSrxKGJjbq2r9wrK1eONiIn1Pu-4Q9rROLBW4HQ,15886
71
+ jarvis/jarvis_multi_agent/__init__.py,sha256=frH5ufVMasKFz_jbVsBk2wH5nq4D0sfDcmfBWQ-DLoA,16726
73
72
  jarvis/jarvis_multi_agent/main.py,sha256=vqLzHesgQkpScVvbgOSyi6X7XoEDQYq3dJ9gZfGiPZw,3351
74
73
  jarvis/jarvis_platform/__init__.py,sha256=WLQHSiE87PPket2M50_hHzjdMIgPIBx2VF8JfB_NNRk,105
75
74
  jarvis/jarvis_platform/ai8.py,sha256=g8JkqPGs9SEbqstNMCc5rCHO0QcPHX9LNvb7HMWwB-Q,11471
@@ -109,11 +108,11 @@ jarvis/jarvis_tools/generate_new_tool.py,sha256=tJz0YtfDwyH9y00VEWw3Btqr9JCNhvtI
109
108
  jarvis/jarvis_tools/methodology.py,sha256=_K4GIDUodGEma3SvNRo7Qs5rliijgNespVLyAPN35JU,5233
110
109
  jarvis/jarvis_tools/read_code.py,sha256=F1RuO0c69t0h7CvrUGqrTyNcOCcUrFQPACc61O_YSso,6382
111
110
  jarvis/jarvis_tools/read_webpage.py,sha256=dfyXJ9vaX-ZRbua1P5ZlaU_SlSzKkeNw-1kI_3-gxFE,5433
112
- jarvis/jarvis_tools/registry.py,sha256=yVXBrJ7plyn7Dr3dD6mPmgd6eiBftmd19Cc84-PwVN8,33312
111
+ jarvis/jarvis_tools/registry.py,sha256=9gIhiyh7jAAnLeGBIDyUUHwOeDtgOx3UUWk6kRN1sJg,33670
113
112
  jarvis/jarvis_tools/retrieve_memory.py,sha256=hhhGSr7jebPHICY9oEKICyI8mfqsRtKjh58qZNZApKc,8624
114
113
  jarvis/jarvis_tools/save_memory.py,sha256=RQtNxcpU53FFv_EBjH0i0oyQ7jWubm-trD1BHuqaGjI,6985
115
114
  jarvis/jarvis_tools/search_web.py,sha256=Hi8WBxcRH02qjOF1qcJP2qSqs3kVOKGFAARfh548Ii4,6370
116
- jarvis/jarvis_tools/sub_agent.py,sha256=Qjr5Ikul0gmtwGehCjy1yOSsbwE9hWiGQvwrOWPef_g,8028
115
+ jarvis/jarvis_tools/sub_agent.py,sha256=Fn2RZ7jLD4cZCWt0HnpSLkdIbeqeZq-1h97Nfg2RAqE,8485
117
116
  jarvis/jarvis_tools/sub_code_agent.py,sha256=KpwTCU89kq_RGNpfyA1C0bTl-f0dTvsCyDKplU-hrds,9600
118
117
  jarvis/jarvis_tools/virtual_tty.py,sha256=L7-J00ARQvIa25T45Hhqg2eCBl6W2LFgqDlWMWf-7dk,25275
119
118
  jarvis/jarvis_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -121,21 +120,21 @@ jarvis/jarvis_tools/cli/main.py,sha256=WL2GNV7WqYl7G1-btRGvCkzDCMk4fPfNvzCrnUFVP
121
120
  jarvis/jarvis_utils/__init__.py,sha256=67h0ldisGlh3oK4DAeNEL2Bl_VsI3tSmfclasyVlueM,850
122
121
  jarvis/jarvis_utils/builtin_replace_map.py,sha256=z8iAqsbZUiGFaozxG1xSu128op8udqHOeEw-GxNt4bU,1708
123
122
  jarvis/jarvis_utils/clipboard.py,sha256=D3wzQeqg_yiH7Axs4d6MRxyNa9XxdnenH-ND2uj2WVQ,2967
124
- jarvis/jarvis_utils/config.py,sha256=Pk-ddQELZ5-vJgRdkwrBbstJJghhGsD_5BtNk5-UO3k,22638
123
+ jarvis/jarvis_utils/config.py,sha256=jOVCrlD597U6FTu8j-lyO93Mpoyqv3Do7Seja_XrRfE,22580
125
124
  jarvis/jarvis_utils/embedding.py,sha256=x6mrkL7Bc3qgfuBDsjc4fg4nKG8ofGxVLVVydbsb8PY,2838
126
125
  jarvis/jarvis_utils/file_processors.py,sha256=XiM248SHS7lLgQDCbORVFWqinbVDUawYxWDOsLXDxP8,3043
127
126
  jarvis/jarvis_utils/fzf.py,sha256=vCs0Uh5dUqGbWzXn2JCtLLCOYE2B39ZNdNveR9PK4DA,1681
128
127
  jarvis/jarvis_utils/git_utils.py,sha256=zxjdxbFb_X6aYo-w1fbMx3d2n1ScbmmaAYlE3wGaaSg,24071
129
128
  jarvis/jarvis_utils/globals.py,sha256=7Xvf9HY6jYJL4vSD1F1WCoxHkHCAyltJUYt4V9gGVU4,8865
130
129
  jarvis/jarvis_utils/http.py,sha256=eRhV3-GYuWmQ0ogq9di9WMlQkFcVb1zGCrySnOgT1x0,4392
131
- jarvis/jarvis_utils/input.py,sha256=muopeZc68N-_tM7ERI2aazqernLumF-sQqS_JfWI0jI,36879
130
+ jarvis/jarvis_utils/input.py,sha256=4VXpUZoAocW1mldlZd4bmXI8a_CmcQj7IPLBNgNLGSI,40045
132
131
  jarvis/jarvis_utils/methodology.py,sha256=z_renvRGgHiC-XTQPuN6rvrJ_ffHlwxK_b1BU_jmNAQ,12800
133
132
  jarvis/jarvis_utils/output.py,sha256=y2fVcao_2ZowFl0IxUrJZCi8T6ZM0z-iPzpk8T8eLxc,13623
134
133
  jarvis/jarvis_utils/tag.py,sha256=f211opbbbTcSyzCDwuIK_oCnKhXPNK-RknYyGzY1yD0,431
135
134
  jarvis/jarvis_utils/utils.py,sha256=uMtfaStxDtp2i9AFIxwtPKgSxLwQxw8Z2rXsX-ZGlis,72728
136
- jarvis_ai_assistant-0.4.1.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
137
- jarvis_ai_assistant-0.4.1.dist-info/METADATA,sha256=DR0N8vMawLJe7n5NdIYeeuE-cFC2xBqxXiBvVcUT148,18751
138
- jarvis_ai_assistant-0.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
139
- jarvis_ai_assistant-0.4.1.dist-info/entry_points.txt,sha256=4GcWKFxRJD-QU14gw_3ZaW4KuEVxOcZK9i270rwPdjA,1395
140
- jarvis_ai_assistant-0.4.1.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
141
- jarvis_ai_assistant-0.4.1.dist-info/RECORD,,
135
+ jarvis_ai_assistant-0.4.2.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
136
+ jarvis_ai_assistant-0.4.2.dist-info/METADATA,sha256=etNq0w20riwTP8aha2pt_wXBdC6SZHhrjTVeV5K4K38,18751
137
+ jarvis_ai_assistant-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
138
+ jarvis_ai_assistant-0.4.2.dist-info/entry_points.txt,sha256=4GcWKFxRJD-QU14gw_3ZaW4KuEVxOcZK9i270rwPdjA,1395
139
+ jarvis_ai_assistant-0.4.2.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
140
+ jarvis_ai_assistant-0.4.2.dist-info/RECORD,,
@@ -1,100 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- AgentConfig: 聚合 Agent 的初始化配置并提供默认值解析。
4
-
5
- 目标(阶段一,最小变更):
6
- - 提供独立的配置承载类,封装 __init__ 中的配置项
7
- - 支持从全局配置与上下文推导默认值
8
- - 暂不强制替换 Agent 的现有参数与流程,后续逐步接入
9
- """
10
- from dataclasses import dataclass, field
11
- from typing import List, Optional
12
-
13
- from jarvis.jarvis_agent.prompts import DEFAULT_SUMMARY_PROMPT
14
- from jarvis.jarvis_utils.config import (
15
- get_max_token_count,
16
- is_execute_tool_confirm,
17
- is_force_save_memory,
18
- is_use_analysis,
19
- is_use_methodology,
20
- is_non_interactive,
21
- )
22
-
23
-
24
- @dataclass
25
- class AgentConfig:
26
- # 核心身份与系统参数
27
- system_prompt: str
28
- name: str = "Jarvis"
29
- description: str = ""
30
- model_group: Optional[str] = None
31
-
32
- # 运行行为
33
- auto_complete: bool = False
34
- non_interactive: bool = False
35
- need_summary: bool = True
36
-
37
- # 可选配置(None 表示使用默认策略解析)
38
- summary_prompt: Optional[str] = None
39
- execute_tool_confirm: Optional[bool] = None
40
- use_methodology: Optional[bool] = None
41
- use_analysis: Optional[bool] = None
42
- force_save_memory: Optional[bool] = None
43
- files: Optional[List[str]] = field(default_factory=list)
44
- max_token_count: Optional[int] = None
45
-
46
- def resolve_defaults(self) -> "AgentConfig":
47
- """
48
- 解析并填充默认值,返回新的 AgentConfig 实例,不修改原对象。
49
- 策略与 Agent._init_config 中的逻辑保持一致,确保兼容。
50
- """
51
- # 复制当前实例的浅拷贝数据
52
- cfg = AgentConfig(
53
- system_prompt=self.system_prompt,
54
- name=self.name,
55
- description=self.description,
56
- model_group=self.model_group,
57
- auto_complete=self.auto_complete,
58
- non_interactive=self.non_interactive,
59
- need_summary=self.need_summary,
60
- summary_prompt=self.summary_prompt,
61
- execute_tool_confirm=self.execute_tool_confirm,
62
- use_methodology=self.use_methodology,
63
- use_analysis=self.use_analysis,
64
- force_save_memory=self.force_save_memory,
65
- files=list(self.files or []),
66
- max_token_count=self.max_token_count,
67
- )
68
-
69
- # use_methodology: 若存在上传文件则禁用;否则按照外部传入或全局默认
70
- if cfg.files:
71
- cfg.use_methodology = False
72
- elif cfg.use_methodology is None:
73
- cfg.use_methodology = is_use_methodology()
74
-
75
- # use_analysis
76
- if cfg.use_analysis is None:
77
- cfg.use_analysis = is_use_analysis()
78
-
79
- # execute_tool_confirm
80
- if cfg.execute_tool_confirm is None:
81
- cfg.execute_tool_confirm = is_execute_tool_confirm()
82
-
83
- # summary_prompt
84
- # Treat None or whitespace-only string as unset, fallback to default
85
- if cfg.summary_prompt is None or (isinstance(cfg.summary_prompt, str) and cfg.summary_prompt.strip() == ""):
86
- cfg.summary_prompt = DEFAULT_SUMMARY_PROMPT
87
-
88
- # max_token_count
89
- if cfg.max_token_count is None:
90
- cfg.max_token_count = get_max_token_count(cfg.model_group)
91
-
92
- # force_save_memory
93
- if cfg.force_save_memory is None:
94
- cfg.force_save_memory = is_force_save_memory()
95
-
96
- # 非交互模式下默认开启自动完成
97
- if is_non_interactive():
98
- cfg.auto_complete = True
99
-
100
- return cfg