jarvis-ai-assistant 0.3.24__py3-none-any.whl → 0.3.26__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.3.24"
4
+ __version__ = "0.3.26"
@@ -662,6 +662,10 @@ class Agent:
662
662
  if self.use_analysis:
663
663
  self.task_analyzer.analysis_task(satisfaction_feedback)
664
664
 
665
+ # 当开启强制保存记忆时,在分析步骤之后触发一次记忆保存
666
+ if self.force_save_memory:
667
+ self.memory_manager.prompt_memory_save()
668
+
665
669
  self._check_and_organize_memory()
666
670
 
667
671
  if self.need_summary:
@@ -384,21 +384,21 @@ def handle_builtin_config_selector(
384
384
  table.add_column("描述", style="white")
385
385
 
386
386
  for idx, opt in enumerate(options, 1):
387
- category = opt.get("category", "")
388
- name = opt.get("name", "")
389
- file_path = opt.get("file", "")
387
+ category = str(opt.get("category", ""))
388
+ name = str(opt.get("name", ""))
389
+ file_path = str(opt.get("file", ""))
390
390
  # 描述列显示配置描述;若为 roles 同时显示角色数量与列表
391
391
  if category == "roles":
392
392
  count = opt.get("roles_count")
393
- details = opt.get("details", "")
394
- parts = []
393
+ details_val = opt.get("details", "")
394
+ parts: list[str] = []
395
395
  if isinstance(count, int) and count > 0:
396
396
  parts.append(f"{count} 个角色")
397
- if details:
398
- parts.append(details)
397
+ if isinstance(details_val, str) and details_val:
398
+ parts.append(details_val)
399
399
  desc_display = "\n".join(parts) if parts else ""
400
400
  else:
401
- desc_display = opt.get("desc", "")
401
+ desc_display = str(opt.get("desc", ""))
402
402
  table.add_row(str(idx), category, name, file_path, desc_display)
403
403
 
404
404
  Console().print(table)
@@ -412,29 +412,29 @@ def handle_builtin_config_selector(
412
412
  index = int(choice.strip())
413
413
  if 1 <= index <= len(options):
414
414
  sel = options[index - 1]
415
- args = []
415
+ args: list[str] = []
416
416
 
417
417
  if sel["category"] == "agent":
418
418
  # jarvis-agent 支持 -f/--config(全局配置)与 -c/--agent-definition
419
- args = [sel["cmd"], "-c", sel["file"]]
419
+ args = [str(sel["cmd"]), "-c", str(sel["file"])]
420
420
  if model_group:
421
- args += ["-g", model_group]
421
+ args += ["-g", str(model_group)]
422
422
  if config_file:
423
- args += ["-f", config_file]
423
+ args += ["-f", str(config_file)]
424
424
  if task:
425
- args += ["--task", task]
425
+ args += ["--task", str(task)]
426
426
 
427
427
  elif sel["category"] == "multi_agent":
428
428
  # jarvis-multi-agent 需要 -c/--config,用户输入通过 -i/--input 传递
429
- args = [sel["cmd"], "-c", sel["file"]]
429
+ args = [str(sel["cmd"]), "-c", str(sel["file"])]
430
430
  if task:
431
- args += ["-i", task]
431
+ args += ["-i", str(task)]
432
432
 
433
433
  elif sel["category"] == "roles":
434
434
  # jarvis-platform-manager role 子命令,支持 -c/-t/-g
435
- args = [sel["cmd"], "role", "-c", sel["file"]]
435
+ args = [str(sel["cmd"]), "role", "-c", str(sel["file"])]
436
436
  if model_group:
437
- args += ["-g", model_group]
437
+ args += ["-g", str(model_group)]
438
438
 
439
439
  if args:
440
440
  PrettyOutput.print(
@@ -165,6 +165,13 @@
165
165
  "JARVIS_MAX_INPUT_TOKEN_COUNT": {
166
166
  "type": "number",
167
167
  "default": 32000
168
+ },
169
+ "ENV": {
170
+ "type": "object",
171
+ "description": "该模型组特定的环境变量,会覆盖全局 ENV 配置",
172
+ "additionalProperties": {
173
+ "type": "string"
174
+ }
168
175
  }
169
176
  },
170
177
  "required": [
@@ -113,9 +113,9 @@ class GitCommitTool:
113
113
  # 获取文件列表
114
114
  files_cmd = ["git", "diff", "--cached", "--name-only"]
115
115
  process = subprocess.Popen(
116
- files_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
116
+ files_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
117
117
  )
118
- files_output = process.communicate()[0].decode()
118
+ files_output = process.communicate()[0]
119
119
  files = [f.strip() for f in files_output.split("\n") if f.strip()]
120
120
  file_count = len(files)
121
121
 
@@ -124,8 +124,9 @@ class GitCommitTool:
124
124
  ["git", "diff", "--cached", "--exit-code"],
125
125
  stdout=subprocess.PIPE,
126
126
  stderr=subprocess.PIPE,
127
+ text=True,
127
128
  )
128
- diff = process.communicate()[0].decode(errors="ignore")
129
+ diff = process.communicate()[0]
129
130
 
130
131
  try:
131
132
  temp_diff_file_path = None
@@ -9,6 +9,7 @@ from typing_extensions import Self
9
9
  from rich import box # type: ignore
10
10
  from rich.live import Live # type: ignore
11
11
  from rich.panel import Panel # type: ignore
12
+ from rich.status import Status # type: ignore
12
13
  from rich.text import Text # type: ignore
13
14
 
14
15
  from jarvis.jarvis_utils.config import (
@@ -120,23 +121,44 @@ class BasePlatform(ABC):
120
121
  else:
121
122
  response = ""
122
123
 
123
- text_content = Text(overflow="fold") # 添加文本溢出处理
124
- panel = Panel(
125
- text_content,
126
- title=f"[bold cyan]{self.name()}[/bold cyan]",
127
- subtitle="[dim]思考中... (按 Ctrl+C 中断)[/dim]",
128
- border_style="bright_blue",
129
- box=box.ROUNDED,
130
- expand=True # 允许面板自动调整大小
131
- )
132
-
133
124
  if not self.suppress_output:
134
125
  if get_pretty_output():
135
- # 优化Live输出,减少闪烁
126
+ chat_iterator = self.chat(message)
127
+ first_chunk = None
128
+
129
+ with Status(
130
+ f"🤔 {self.name()} 正在思考中...", spinner="dots", console=console
131
+ ):
132
+ try:
133
+ while True:
134
+ first_chunk = next(chat_iterator)
135
+ if first_chunk:
136
+ break
137
+ except StopIteration:
138
+ return ""
139
+
140
+ text_content = Text(overflow="fold")
141
+ panel = Panel(
142
+ text_content,
143
+ title=f"[bold cyan]{self.name()}[/bold cyan]",
144
+ subtitle="[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]",
145
+ border_style="bright_blue",
146
+ box=box.ROUNDED,
147
+ expand=True, # 允许面板自动调整大小
148
+ )
149
+
136
150
  buffer = []
137
151
  buffer_count = 0
138
152
  with Live(panel, refresh_per_second=4, transient=False) as live:
139
- for s in self.chat(message):
153
+ # Process first chunk
154
+ response += first_chunk
155
+ buffer.append(first_chunk)
156
+ buffer_count += 1
157
+
158
+ # Process rest of the chunks
159
+ for s in chat_iterator:
160
+ if not s:
161
+ continue
140
162
  response += s # Accumulate the full response string
141
163
  buffer.append(s)
142
164
  buffer_count += 1
@@ -144,13 +166,17 @@ class BasePlatform(ABC):
144
166
  # 积累一定量或达到最后再更新,减少闪烁
145
167
  if buffer_count >= 5 or s == "":
146
168
  # Append buffered content to the Text object
147
- text_content.append("".join(buffer), style="bright_white")
169
+ text_content.append(
170
+ "".join(buffer), style="bright_white"
171
+ )
148
172
  buffer.clear()
149
173
  buffer_count = 0
150
174
 
151
175
  # --- Scrolling Logic ---
152
176
  # Calculate available height in the panel
153
- max_text_height = console.height - 5 # Leave space for borders/titles
177
+ max_text_height = (
178
+ console.height - 5
179
+ ) # Leave space for borders/titles
154
180
  if max_text_height <= 0:
155
181
  max_text_height = 1
156
182
 
@@ -163,11 +189,15 @@ class BasePlatform(ABC):
163
189
  # If content overflows, truncate to show only the last few lines
164
190
  if len(lines) > max_text_height:
165
191
  new_text = "\n".join(
166
- text_content.plain.splitlines()[-max_text_height:]
192
+ text_content.plain.splitlines()[
193
+ -max_text_height:
194
+ ]
167
195
  )
168
196
  text_content.plain = new_text
169
197
 
170
- panel.subtitle = "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
198
+ panel.subtitle = (
199
+ "[yellow]正在回答... (按 Ctrl+C 中断)[/yellow]"
200
+ )
171
201
  live.update(panel)
172
202
 
173
203
  if is_immediate_abort() and get_interrupt():
@@ -175,7 +205,9 @@ class BasePlatform(ABC):
175
205
 
176
206
  # Ensure any remaining content in the buffer is displayed
177
207
  if buffer:
178
- text_content.append("".join(buffer), style="bright_white")
208
+ text_content.append(
209
+ "".join(buffer), style="bright_white"
210
+ )
179
211
 
180
212
  # At the end, display the entire response
181
213
  text_content.plain = response
jarvis/jarvis_rag/cli.py CHANGED
@@ -14,7 +14,7 @@ from langchain_community.document_loaders import (
14
14
  from langchain_core.document_loaders.base import BaseLoader
15
15
  from rich.markdown import Markdown
16
16
 
17
- from jarvis.jarvis_utils.utils import init_env
17
+ from jarvis.jarvis_utils.utils import init_env, is_rag_installed, get_missing_rag_modules
18
18
  from jarvis.jarvis_utils.config import (
19
19
  get_rag_embedding_model,
20
20
  get_rag_use_bm25,
@@ -357,6 +357,12 @@ def retrieve(
357
357
  None, "--db-path", help="向量数据库的路径。覆盖全局配置。"
358
358
  ),
359
359
  n_results: int = typer.Option(5, "--top-n", help="要检索的文档数量。"),
360
+ rewrite: bool = typer.Option(
361
+ True,
362
+ "--rewrite/--no-rewrite",
363
+ help="是否对查询进行LLM重写以提升召回,默认开启。",
364
+ show_default=True,
365
+ ),
360
366
  ):
361
367
  """仅从RAG知识库检索文档并打印结果。"""
362
368
  try:
@@ -371,6 +377,7 @@ def retrieve(
371
377
  collection_name=collection_name,
372
378
  use_bm25=use_bm25,
373
379
  use_rerank=use_rerank,
380
+ use_query_rewrite=rewrite,
374
381
  )
375
382
 
376
383
  PrettyOutput.print(f"正在为问题检索文档: '{question}'", OutputType.INFO)
@@ -462,19 +469,15 @@ def query(
462
469
  raise typer.Exit(code=1)
463
470
 
464
471
 
465
- _RAG_INSTALLED = False
466
- try:
467
- import langchain # noqa
468
472
 
469
- _RAG_INSTALLED = True
470
- except ImportError:
471
- pass
472
473
 
473
474
 
474
475
  def _check_rag_dependencies():
475
- if not _RAG_INSTALLED:
476
+ if not is_rag_installed():
477
+ missing = get_missing_rag_modules()
478
+ missing_str = f"缺少依赖: {', '.join(missing)}。" if missing else ""
476
479
  PrettyOutput.print(
477
- "RAG依赖项未安装。请运行 'pip install \"jarvis-ai-assistant[rag]\"' 来使用此命令。",
480
+ f"RAG依赖项未安装或不完整。{missing_str}请运行 'pip install \"jarvis-ai-assistant[rag]\"' 后重试。",
478
481
  OutputType.ERROR,
479
482
  )
480
483
  raise typer.Exit(code=1)
@@ -1,6 +1,9 @@
1
1
  import torch
2
+ import os
2
3
  from typing import List, cast
3
4
  from langchain_huggingface import HuggingFaceEmbeddings
5
+ from huggingface_hub import snapshot_download
6
+
4
7
 
5
8
  from .cache import EmbeddingCache
6
9
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
@@ -38,18 +41,135 @@ class EmbeddingManager:
38
41
  encode_kwargs = {"normalize_embeddings": True}
39
42
 
40
43
  try:
41
- # First try to load model locally
44
+ # First try to load model from local cache without any network access
42
45
  try:
43
46
  from sentence_transformers import SentenceTransformer
44
- local_model = SentenceTransformer(self.model_name, device=model_kwargs["device"])
47
+ local_dir = None
48
+ # Prefer explicit local dir via env or direct path
49
+
50
+ if os.path.isdir(self.model_name):
51
+ return HuggingFaceEmbeddings(
52
+ model_name=self.model_name,
53
+ model_kwargs=model_kwargs,
54
+ encode_kwargs=encode_kwargs,
55
+ show_progress=False,
56
+ )
57
+
58
+ # Try common local cache directories for sentence-transformers and HF hub
59
+ try:
60
+ home = os.path.expanduser("~")
61
+ st_home = os.path.join(home, ".cache", "sentence_transformers")
62
+ torch_st_home = os.path.join(home, ".cache", "torch", "sentence_transformers")
63
+ # Build common name variants found in local caches
64
+ org, name = (
65
+ self.model_name.split("/", 1)
66
+ if "/" in self.model_name
67
+ else ("", self.model_name)
68
+ )
69
+ san1 = self.model_name.replace("/", "_")
70
+ san2 = self.model_name.replace("/", "__")
71
+ san3 = self.model_name.replace("/", "--")
72
+ # include plain 'name' for caches that drop org prefix
73
+ name_variants = list(dict.fromkeys([self.model_name, san1, san2, san3, name]))
74
+ candidates = []
75
+ for base in [st_home, torch_st_home]:
76
+ for nv in name_variants:
77
+ p = os.path.join(base, nv)
78
+ if os.path.isdir(p):
79
+ candidates.append(p)
80
+ # Fuzzy scan cache directory for entries that include variants
81
+ try:
82
+ for entry in os.listdir(base):
83
+ ep = os.path.join(base, entry)
84
+ if not os.path.isdir(ep):
85
+ continue
86
+ if (
87
+ (org and entry.startswith(f"{org}__") and name in entry)
88
+ or (san1 in entry)
89
+ or (name in entry)
90
+ ):
91
+ candidates.append(ep)
92
+ except Exception:
93
+ pass
94
+
95
+ # Hugging Face Hub cache snapshots
96
+ hf_cache = os.path.join(home, ".cache", "huggingface", "hub")
97
+ if "/" in self.model_name:
98
+ org, name = self.model_name.split("/", 1)
99
+ models_dir = os.path.join(hf_cache, f"models--{org}--{name}", "snapshots")
100
+ if os.path.isdir(models_dir):
101
+ try:
102
+ snaps = sorted(
103
+ [os.path.join(models_dir, d) for d in os.listdir(models_dir)],
104
+ key=lambda p: os.path.getmtime(p),
105
+ reverse=True,
106
+ )
107
+ except Exception:
108
+ snaps = [os.path.join(models_dir, d) for d in os.listdir(models_dir)]
109
+ for sp in snaps:
110
+ if os.path.isdir(sp):
111
+ candidates.append(sp)
112
+ break
113
+
114
+ for cand in candidates:
115
+ try:
116
+ return HuggingFaceEmbeddings(
117
+ model_name=cand,
118
+ model_kwargs=model_kwargs,
119
+ encode_kwargs=encode_kwargs,
120
+ show_progress=False,
121
+ )
122
+ except Exception:
123
+ continue
124
+ except Exception:
125
+ pass
126
+
127
+ try:
128
+ # Try resolve local cached directory; do not hit network
129
+ local_dir = snapshot_download(repo_id=self.model_name, local_files_only=True)
130
+ except Exception:
131
+ local_dir = None
132
+
133
+ if local_dir:
134
+ return HuggingFaceEmbeddings(
135
+ model_name=local_dir,
136
+ model_kwargs=model_kwargs,
137
+ encode_kwargs=encode_kwargs,
138
+ show_progress=False,
139
+ )
140
+
141
+
142
+
143
+ # Fall back to remote download if local cache not found and not offline
45
144
  return HuggingFaceEmbeddings(
46
- client=local_model,
47
145
  model_name=self.model_name,
48
146
  model_kwargs=model_kwargs,
49
147
  encode_kwargs=encode_kwargs,
148
+ show_progress=True,
50
149
  )
51
- except Exception:
52
- # Fall back to remote download if local loading fails
150
+ except Exception as _e:
151
+ # 如果已检测到本地候选路径(直接目录 / 本地缓存快照),则视为本地加载失败,
152
+ # 为避免在用户期望“本地优先不联网”的情况下触发联网,直接抛错并给出修复建议。
153
+ had_local_candidate = False
154
+ try:
155
+ had_local_candidate = (
156
+ os.path.isdir(self.model_name)
157
+ # 如果上面 snapshot_download 命中了本地缓存,会将 local_dir 设为非 None
158
+ or (locals().get("local_dir") is not None)
159
+ )
160
+ except Exception:
161
+ pass
162
+
163
+ if had_local_candidate:
164
+ PrettyOutput.print(
165
+ "检测到本地模型路径但加载失败。为避免触发网络访问,已中止远程回退。\n"
166
+ "请确认本地目录包含完整的 Transformers/Tokenizer 文件(如 config.json、model.safetensors、tokenizer.json/merges.txt 等),\n"
167
+ "或在配置中将 embedding_model 设置为该本地目录,或将模型放置到默认的 Hugging Face 缓存目录(例如 ~/.cache/huggingface/hub)。",
168
+ OutputType.ERROR,
169
+ )
170
+ raise
171
+
172
+ # 未发现任何本地候选,则保持原有行为:回退至远程下载
53
173
  return HuggingFaceEmbeddings(
54
174
  model_name=self.model_name,
55
175
  model_kwargs=model_kwargs,
@@ -34,6 +34,7 @@ class JarvisRAGPipeline:
34
34
  collection_name: str = "jarvis_rag_collection",
35
35
  use_bm25: bool = True,
36
36
  use_rerank: bool = True,
37
+ use_query_rewrite: bool = True,
37
38
  ):
38
39
  """
39
40
  初始化RAG管道。
@@ -69,6 +70,8 @@ class JarvisRAGPipeline:
69
70
  self.collection_name = collection_name
70
71
  self.use_bm25 = use_bm25
71
72
  self.use_rerank = use_rerank
73
+ # 查询重写开关(默认开启,可由CLI控制)
74
+ self.use_query_rewrite = use_query_rewrite
72
75
 
73
76
  # 延迟加载的组件
74
77
  self._embedding_manager: Optional[EmbeddingManager] = None
@@ -229,8 +232,15 @@ class JarvisRAGPipeline:
229
232
  """
230
233
  # 0. 检测索引变更并可选更新(在重写query之前)
231
234
  self._pre_search_update_index_if_needed()
232
- # 1. 将原始查询重写为多个查询
233
- rewritten_queries = self._get_query_rewriter().rewrite(query_text)
235
+ # 1. 将原始查询重写为多个查询(可配置)
236
+ if self.use_query_rewrite:
237
+ rewritten_queries = self._get_query_rewriter().rewrite(query_text)
238
+ else:
239
+ PrettyOutput.print(
240
+ "已关闭查询重写,将直接使用原始查询进行检索。",
241
+ OutputType.INFO,
242
+ )
243
+ rewritten_queries = [query_text]
234
244
 
235
245
  # 2. 为每个重写的查询检索初始候选文档
236
246
  PrettyOutput.print(
@@ -303,8 +313,15 @@ class JarvisRAGPipeline:
303
313
  """
304
314
  # 0. 检测索引变更并可选更新(在重写query之前)
305
315
  self._pre_search_update_index_if_needed()
306
- # 1. 重写查询
307
- rewritten_queries = self._get_query_rewriter().rewrite(query_text)
316
+ # 1. 重写查询(可配置)
317
+ if self.use_query_rewrite:
318
+ rewritten_queries = self._get_query_rewriter().rewrite(query_text)
319
+ else:
320
+ PrettyOutput.print(
321
+ "已关闭查询重写,将直接使用原始查询进行检索。",
322
+ OutputType.INFO,
323
+ )
324
+ rewritten_queries = [query_text]
308
325
 
309
326
  # 2. 检索候选文档
310
327
  PrettyOutput.print(
@@ -1,9 +1,11 @@
1
1
  from typing import List
2
+ import os
2
3
 
3
4
  from langchain.docstore.document import Document
4
5
  from sentence_transformers.cross_encoder import ( # type: ignore
5
6
  CrossEncoder,
6
7
  )
8
+ from huggingface_hub import snapshot_download
7
9
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
8
10
 
9
11
 
@@ -21,8 +23,28 @@ class Reranker:
21
23
  model_name (str): 要使用的Cross-Encoder模型的名称。
22
24
  """
23
25
  PrettyOutput.print(f"正在初始化重排模型: {model_name}...", OutputType.INFO)
24
- self.model = CrossEncoder(model_name)
25
- PrettyOutput.print("重排模型初始化成功。", OutputType.SUCCESS)
26
+ try:
27
+ local_dir = None
28
+
29
+ if os.path.isdir(model_name):
30
+ self.model = CrossEncoder(model_name)
31
+ PrettyOutput.print("重排模型初始化成功。", OutputType.SUCCESS)
32
+ return
33
+ try:
34
+ # Prefer local cache; avoid any network access
35
+ local_dir = snapshot_download(repo_id=model_name, local_files_only=True)
36
+ except Exception:
37
+ local_dir = None
38
+
39
+ if local_dir:
40
+ self.model = CrossEncoder(local_dir)
41
+ else:
42
+ self.model = CrossEncoder(model_name)
43
+
44
+ PrettyOutput.print("重排模型初始化成功。", OutputType.SUCCESS)
45
+ except Exception as e:
46
+ PrettyOutput.print(f"初始化重排模型失败: {e}", OutputType.ERROR)
47
+ raise
26
48
 
27
49
  def rerank(
28
50
  self, query: str, documents: List[Document], top_n: int = 5
@@ -24,20 +24,11 @@ Example:
24
24
 
25
25
  def execute_command(command: str, should_run: bool) -> None:
26
26
  """Print command without execution"""
27
- PrettyOutput.print(command, OutputType.CODE, lang="bash")
27
+ print(command)
28
28
  if should_run:
29
29
  os.system(command)
30
30
 
31
31
 
32
- def _check_fish_shell() -> bool:
33
- """Check if current shell is fish
34
-
35
- Returns:
36
- bool: True if fish shell, False otherwise
37
- """
38
- return get_shell_name() == "fish"
39
-
40
-
41
32
  def _get_config_file() -> str:
42
33
  """Get fish config file path
43
34
 
@@ -122,8 +122,8 @@ class FileSearchReplaceTool:
122
122
 
123
123
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
124
124
 
125
- stdout_messages = []
126
- stderr_messages = []
125
+ stdout_messages: list[str] = []
126
+ stderr_messages: list[str] = []
127
127
  overall_success = False
128
128
  file_results = []
129
129
 
@@ -168,10 +168,10 @@ class FileSearchReplaceTool:
168
168
  )
169
169
 
170
170
  # 整合所有错误信息到stderr
171
- all_stderr = []
172
- for result in file_results:
173
- if not result["success"]:
174
- all_stderr.append(f"文件 {result['file']} 处理失败: {result['stderr']}")
171
+ all_stderr: list[str] = []
172
+ for file_result in file_results:
173
+ if not file_result["success"]:
174
+ all_stderr.append(f"文件 {file_result['file']} 处理失败: {file_result['stderr']}")
175
175
 
176
176
  return {
177
177
  "success": overall_success,
@@ -74,8 +74,7 @@ class ScriptTool:
74
74
  stream.feed(data)
75
75
 
76
76
  # 清理每行右侧空格,并过滤空行
77
- cleaned = []
78
- cleaned = []
77
+ cleaned: list[str] = []
79
78
  for y in range(screen.lines):
80
79
  line = screen.buffer[y]
81
80
  stripped = "".join(char.data for char in line.values()).rstrip()
@@ -64,12 +64,6 @@ class SubAgentTool:
64
64
  f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
65
65
  )
66
66
 
67
- # 读取背景信息并组合任务
68
- background: str = str(args.get("background", "")).strip()
69
- enhanced_task = (
70
- f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
71
- )
72
-
73
67
  # 继承父Agent的运行参数(用于覆盖默认值);若无父Agent则使用默认/全局配置
74
68
  parent_agent = args.get("agent")
75
69
  # 如未注入父Agent,尝试从全局获取当前或任一已注册Agent
@@ -160,7 +154,10 @@ class SubAgentTool:
160
154
  try:
161
155
  model_name = parent_agent.model.name() # type: ignore[attr-defined]
162
156
  if model_name:
163
- agent.model.set_model_name(model_name) # type: ignore[attr-defined]
157
+ from typing import Any
158
+ model_obj: Any = getattr(agent, "model", None)
159
+ if model_obj is not None:
160
+ model_obj.set_model_name(model_name)
164
161
  except Exception:
165
162
  pass
166
163
  if use_tools:
@@ -161,7 +161,10 @@ class SubCodeAgentTool:
161
161
  try:
162
162
  parent_model_name = parent_agent.model.name() # type: ignore[attr-defined]
163
163
  if parent_model_name:
164
- code_agent.agent.model.set_model_name(parent_model_name) # type: ignore[attr-defined]
164
+ from typing import Any
165
+ model_obj: Any = getattr(code_agent.agent, "model", None)
166
+ if model_obj is not None:
167
+ model_obj.set_model_name(parent_model_name)
165
168
  except Exception:
166
169
  pass
167
170
  except Exception:
@@ -115,6 +115,14 @@ def get_shell_name() -> str:
115
115
  return os.path.basename(shell_path).lower()
116
116
 
117
117
 
118
+ def _apply_llm_group_env_override(group_config: Dict[str, Any]) -> None:
119
+ """如果模型组配置中包含ENV,则应用环境变量覆盖"""
120
+ if "ENV" in group_config and isinstance(group_config["ENV"], dict):
121
+ os.environ.update(
122
+ {str(k): str(v) for k, v in group_config["ENV"].items() if v is not None}
123
+ )
124
+
125
+
118
126
  def _get_resolved_model_config(
119
127
  model_group_override: Optional[str] = None,
120
128
  ) -> Dict[str, Any]:
@@ -141,6 +149,8 @@ def _get_resolved_model_config(
141
149
  if isinstance(group_item, dict) and model_group_name in group_item:
142
150
  group_config = group_item[model_group_name]
143
151
  break
152
+
153
+ _apply_llm_group_env_override(group_config)
144
154
 
145
155
  # Start with group config
146
156
  resolved_config = group_config.copy()
@@ -19,6 +19,7 @@ from typing import Any, Dict, List, Set, Tuple
19
19
  from jarvis.jarvis_utils.config import get_data_dir, is_confirm_before_apply_patch
20
20
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
21
21
  from jarvis.jarvis_utils.input import user_confirm
22
+ from jarvis.jarvis_utils.utils import is_rag_installed
22
23
 
23
24
 
24
25
  def find_git_root_and_cd(start_dir: str = ".") -> str:
@@ -427,14 +428,8 @@ def check_and_update_git_repo(repo_path: str) -> bool:
427
428
  is_uv_env = True
428
429
 
429
430
  # 根据环境选择安装命令
430
- # 检测是否安装了 RAG 特性
431
- rag_installed = False
432
- try:
433
- import langchain # noqa
434
-
435
- rag_installed = True
436
- except ImportError:
437
- pass
431
+ # 检测是否安装了 RAG 特性(更精确)
432
+ rag_installed = is_rag_installed()
438
433
 
439
434
  # 根据环境和 RAG 特性选择安装命令
440
435
  if rag_installed:
@@ -702,7 +702,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
702
702
  "未检测到 fzf,无法打开文件选择器。", OutputType.WARNING
703
703
  )
704
704
  else:
705
- files: list[str] = []
705
+ files = []
706
706
  try:
707
707
  r = subprocess.run(
708
708
  ["git", "ls-files"],
@@ -835,7 +835,7 @@ def get_multiline_input(tip: str, print_on_empty: bool = True) -> str:
835
835
  "未检测到 fzf,无法打开文件选择器。", OutputType.WARNING
836
836
  )
837
837
  else:
838
- files: list[str] = []
838
+ files = []
839
839
  try:
840
840
  import os as _os
841
841
 
@@ -65,6 +65,41 @@ COMMAND_MAPPING = {
65
65
  "jmo": "jarvis-memory-organizer",
66
66
  }
67
67
 
68
+ # RAG 依赖检测工具函数(更精确)
69
+ _RAG_REQUIRED_MODULES = [
70
+ "langchain",
71
+ "langchain_community",
72
+ "chromadb",
73
+ "sentence_transformers",
74
+ "rank_bm25",
75
+ "unstructured",
76
+ ]
77
+ _RAG_OPTIONAL_MODULES = [
78
+ "langchain_huggingface",
79
+ ]
80
+
81
+
82
+ def get_missing_rag_modules() -> List[str]:
83
+ """
84
+ 返回缺失的 RAG 关键依赖模块列表。
85
+ 仅检查必要模块,不导入模块,避免副作用。
86
+ """
87
+ try:
88
+ from importlib.util import find_spec
89
+
90
+ missing = [m for m in _RAG_REQUIRED_MODULES if find_spec(m) is None]
91
+ return missing
92
+ except Exception:
93
+ # 任何异常都视为无法确认,保持保守策略
94
+ return _RAG_REQUIRED_MODULES[:] # 视为全部缺失
95
+
96
+
97
+ def is_rag_installed() -> bool:
98
+ """
99
+ 更准确的 RAG 安装检测:确认关键依赖模块均可用。
100
+ """
101
+ return len(get_missing_rag_modules()) == 0
102
+
68
103
 
69
104
  def _setup_signal_handler() -> None:
70
105
  """设置SIGINT信号处理函数"""
@@ -139,14 +174,11 @@ def _check_pip_updates() -> bool:
139
174
  if uv_path.exists():
140
175
  is_uv_env = True
141
176
 
142
- # 检测是否安装了 RAG 特性
143
- rag_installed = False
144
- try:
145
- import langchain # type: ignore # noqa
146
-
147
- rag_installed = True
148
- except ImportError:
149
- pass
177
+ # 检测是否安装了 RAG 特性(更精确)
178
+ from jarvis.jarvis_utils.utils import (
179
+ is_rag_installed as _is_rag_installed,
180
+ ) # 延迟导入避免潜在循环依赖
181
+ rag_installed = _is_rag_installed()
150
182
 
151
183
  # 提供更新命令
152
184
  package_spec = (
@@ -1085,6 +1117,14 @@ def _collect_optional_config_interactively(
1085
1117
  )
1086
1118
  or changed
1087
1119
  )
1120
+ changed = (
1121
+ _ask_and_set_int(
1122
+ "JARVIS_TOOL_FILTER_THRESHOLD",
1123
+ "设置AI工具筛选阈值 (当可用工具数超过此值时触发AI筛选, 默认30)",
1124
+ 30,
1125
+ )
1126
+ or changed
1127
+ )
1088
1128
 
1089
1129
  # 目录类配置(逗号分隔)
1090
1130
  changed = (
@@ -1164,7 +1204,7 @@ def _collect_optional_config_interactively(
1164
1204
  try:
1165
1205
  if "JARVIS_RAG" not in config_data:
1166
1206
  if get_yes_no("是否配置 RAG 检索增强参数?", default=False):
1167
- rag_conf = {}
1207
+ rag_conf: Dict[str, Any] = {}
1168
1208
  emb = get_single_line_input(
1169
1209
  f"RAG 嵌入模型(留空使用默认: {rag_default_embed}):",
1170
1210
  default="",
@@ -1468,20 +1508,15 @@ def while_success(func: Callable[[], Any], sleep_time: float = 0.1) -> Any:
1468
1508
  返回:
1469
1509
  函数执行结果
1470
1510
  """
1471
- logs: List[str] = []
1472
1511
  result: Any = None
1473
- success = False
1474
1512
  while True:
1475
1513
  try:
1476
1514
  result = func()
1477
- success = True
1478
1515
  break
1479
1516
  except Exception:
1480
- logs.append(f"重试中,等待 {sleep_time}s...")
1517
+ PrettyOutput.print(f"重试中,等待 {sleep_time}s...", OutputType.WARNING)
1481
1518
  time.sleep(sleep_time)
1482
1519
  continue
1483
- if logs:
1484
- PrettyOutput.print("\n".join(logs), OutputType.WARNING)
1485
1520
  return result
1486
1521
 
1487
1522
 
@@ -1499,16 +1534,13 @@ def while_true(func: Callable[[], bool], sleep_time: float = 0.1) -> Any:
1499
1534
  与while_success不同,此函数只检查返回是否为True,
1500
1535
  不捕获异常,异常会直接抛出
1501
1536
  """
1502
- logs: List[str] = []
1503
1537
  ret: bool = False
1504
1538
  while True:
1505
1539
  ret = func()
1506
1540
  if ret:
1507
1541
  break
1508
- logs.append(f"重试中,等待 {sleep_time}s...")
1542
+ PrettyOutput.print(f"重试中,等待 {sleep_time}s...", OutputType.WARNING)
1509
1543
  time.sleep(sleep_time)
1510
- if logs:
1511
- PrettyOutput.print("\n".join(logs), OutputType.WARNING)
1512
1544
  return ret
1513
1545
 
1514
1546
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.3.24
3
+ Version: 0.3.26
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,11 +1,11 @@
1
- jarvis/__init__.py,sha256=sNrDvwFba715bqcu57BUGzZ7SeFI3Oc-7DTacWo5JxM,74
2
- jarvis/jarvis_agent/__init__.py,sha256=eLSiwd-Eo6KqG0qLJ5peI-symxWUrTsxWCBnOgEO474,37524
1
+ jarvis/__init__.py,sha256=iYbIKGj1PIzlv-YretRu86LOVz9abIwPUZ9co3CM1oo,74
2
+ jarvis/jarvis_agent/__init__.py,sha256=S62A_Mki77-RIli_9uNJvz27wRlu2LL3EsFuZjKBybw,37702
3
3
  jarvis/jarvis_agent/agent_manager.py,sha256=mmDe26Wa1Dnyp8CkTKupcKipC-5HSuCnk-oVJseOdTg,2801
4
4
  jarvis/jarvis_agent/builtin_input_handler.py,sha256=wS-FqpT3pIXwHn1dfL3SpXonUKWgVThbQueUIeyRc2U,2917
5
5
  jarvis/jarvis_agent/config_editor.py,sha256=IHyuwI4SRDrqxz8lO0NNgt2F3uYAw1WEKXMxQV49bYI,1928
6
6
  jarvis/jarvis_agent/edit_file_handler.py,sha256=5sFz84jqy2gpc0aLOre2bvz8_DitlBoWZs_cQwftWLw,11570
7
7
  jarvis/jarvis_agent/file_methodology_manager.py,sha256=PwDUQwq7HVIyPInsN8fgWyMXLwi8heIXPrqfBZJhVHs,4260
8
- jarvis/jarvis_agent/jarvis.py,sha256=gTqu7-3lXBYGXE8cpmu6ypjc_Jm1GmVhsCsOcoDiXS8,22241
8
+ jarvis/jarvis_agent/jarvis.py,sha256=UNI4PoJpf4L7wp9y8-A6AfMXMQv7ikRk2YXmN5pRKUc,22383
9
9
  jarvis/jarvis_agent/main.py,sha256=bFcwXWC6O05jQiXy6ED_bHjdjDo5VwV_i1BoBEAzgP0,3336
10
10
  jarvis/jarvis_agent/memory_manager.py,sha256=Ckt6wmXOduu97jfyWQbRDmNH6yX11XzLW2nMTeVhFeY,5316
11
11
  jarvis/jarvis_agent/methodology_share_manager.py,sha256=AB_J9BwRgaeENQfL6bH83FOLeLrgHhppMb7psJNevKs,6874
@@ -44,11 +44,11 @@ jarvis/jarvis_code_analysis/checklists/shell.py,sha256=aRFYhQQvTgbYd-uY5pc8UHIUA
44
44
  jarvis/jarvis_code_analysis/checklists/sql.py,sha256=vR0T6qC7b4dURjJVAd7kSVxyvZEQXPG1Jqc2sNTGp5c,2355
45
45
  jarvis/jarvis_code_analysis/checklists/swift.py,sha256=TPx4I6Gupvs6tSerRKmTSKEPQpOLEbH2Y7LXg1uBgxc,2566
46
46
  jarvis/jarvis_code_analysis/checklists/web.py,sha256=25gGD7pDadZQybNFvALYxWvK0VRjGQb1NVJQElwjyk0,3943
47
- jarvis/jarvis_data/config_schema.json,sha256=GIBSroMb9YE1oW0rZQRVjj1ceQOOFPxeYXQ4eTnLoL8,12323
47
+ jarvis/jarvis_data/config_schema.json,sha256=_O_FQwmEldoGpTJ0EjyDjI6Zc2wkyr_7yVn00WA_Qf4,12575
48
48
  jarvis/jarvis_data/tiktoken/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
49
49
  jarvis/jarvis_git_squash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  jarvis/jarvis_git_squash/main.py,sha256=6PECdAbTbrsJBRLK1pXBh4hdJ_LADh-XXSic1xJi97E,2255
51
- jarvis/jarvis_git_utils/git_commiter.py,sha256=q9O__HfoGZCV5tSl3lI0Gp2hynHUzXV5jDlRaFYk_6o,14554
51
+ jarvis/jarvis_git_utils/git_commiter.py,sha256=mtrZpdq42567dnX-Q8OJ6ZtSRaqRDDDQxHAGRMXdej0,14559
52
52
  jarvis/jarvis_mcp/__init__.py,sha256=OPMtjD-uq9xAaKCRIDyKIosaFfBe1GBPu1az-mQ0rVM,2048
53
53
  jarvis/jarvis_mcp/sse_mcp_client.py,sha256=UIDBaFNuuaJE0YiKmtbZTqwZpkDI5SaS0my1DIJj-3g,22831
54
54
  jarvis/jarvis_mcp/stdio_mcp_client.py,sha256=APYUksYKlMx7AVNODKOLrTkKZPnp4kqTQIYIuNDDKko,11286
@@ -60,7 +60,7 @@ jarvis/jarvis_multi_agent/__init__.py,sha256=Ygp4DBBkmEXCRcQV3nr07zOxo2Tb4149mi4
60
60
  jarvis/jarvis_multi_agent/main.py,sha256=b9IThFMeUZCYSlgT-VT8r7xeBdrEE_zNT11awEc8IdY,1853
61
61
  jarvis/jarvis_platform/__init__.py,sha256=WLQHSiE87PPket2M50_hHzjdMIgPIBx2VF8JfB_NNRk,105
62
62
  jarvis/jarvis_platform/ai8.py,sha256=g8JkqPGs9SEbqstNMCc5rCHO0QcPHX9LNvb7HMWwB-Q,11471
63
- jarvis/jarvis_platform/base.py,sha256=dnd3nidsf7mqwimAzmQSV9s_wGviQ7VjRqOLUkRVLYc,12151
63
+ jarvis/jarvis_platform/base.py,sha256=5RLs5d4iYQ6PdSgKXj9idoCkln68Kmrv7-Sh7HBGbyw,13434
64
64
  jarvis/jarvis_platform/human.py,sha256=jWjW8prEag79e6ddqTPV4nz_Gz6zFBfO4a1EbvP8QWA,4908
65
65
  jarvis/jarvis_platform/kimi.py,sha256=dLES_E0VmDQ3TwjTZk5vCGdbvdBeSVvvlXR90m6vPfY,15711
66
66
  jarvis/jarvis_platform/openai.py,sha256=0YSeDGHRSPQP2haEzFARx_aZH_d_UZ-HSCsJLh2hW5k,8037
@@ -72,15 +72,15 @@ jarvis/jarvis_platform_manager/main.py,sha256=tU25oVQzEFPB8aOqPec8SgnxsapCFuHBkY
72
72
  jarvis/jarvis_platform_manager/service.py,sha256=WG2r0JRBGfSLGdsKxqYvmaacU_ne3PTn3RkczTzUb_M,14899
73
73
  jarvis/jarvis_rag/__init__.py,sha256=HRTXgnQxDuaE9x-e3r6SYqhJ5d4DSI_rrIxy2IGY6qk,320
74
74
  jarvis/jarvis_rag/cache.py,sha256=Tqx_Oe-AhuWlMXHGHUaIuG6OEHoHBVZq7mL3kldtFFU,2723
75
- jarvis/jarvis_rag/cli.py,sha256=ZuyySys8sV8Mv2B_-WC2pejr2hAbh0c7qhtz0AEEOxw,17219
76
- jarvis/jarvis_rag/embedding_manager.py,sha256=7JAylkU0Y22X5x8kv5WiwMAgU-MSJEZG8PmhrWOZH4k,4307
75
+ jarvis/jarvis_rag/cli.py,sha256=4tcB76I87QNHCSp29JDslETJSH5oWPtNYMlos-6dZu0,17532
76
+ jarvis/jarvis_rag/embedding_manager.py,sha256=SF7BW8hoZWzDhDqlOdhnX6XTw42Fx2GKs7Y_RUT1ydo,10400
77
77
  jarvis/jarvis_rag/llm_interface.py,sha256=DH46Vm3AgzK1o41X7wUUYUbbOOVPgmDCxS5l6djm9eA,4561
78
78
  jarvis/jarvis_rag/query_rewriter.py,sha256=LGAWZ8kwH_dpquuYqc4QWC7IXmHX3SsnPSYMWOn-nDE,4072
79
- jarvis/jarvis_rag/rag_pipeline.py,sha256=sPvCucAshr_luGJLx7WiCfQKVHrrHHpb4-SVdAZdJc8,13085
80
- jarvis/jarvis_rag/reranker.py,sha256=Uzn4n1bNj4kWyQu9-z-jK_5dAU6drn5jEugML-kFHg8,1885
79
+ jarvis/jarvis_rag/rag_pipeline.py,sha256=aaTuxiArtVaPemWeySApw64q204JeLNXpOEtq7Adn1I,13797
80
+ jarvis/jarvis_rag/reranker.py,sha256=7Azc3y5fngwfPKtzZ8tx6iGKNeqC8uDy8yo8VCyLyL4,2670
81
81
  jarvis/jarvis_rag/retriever.py,sha256=BNEFZAgxTbmkxzBP1uy1wz3MX8wJN1wx5EtfsHqakqE,18374
82
82
  jarvis/jarvis_smart_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
- jarvis/jarvis_smart_shell/main.py,sha256=J5O58K5KvUvB_ghbT5XwIi5QGXD3DiBlGDs0wYYvy-w,15197
83
+ jarvis/jarvis_smart_shell/main.py,sha256=JizPfVI-7-GqnYxdg77RHietGet6hZE6xUZoUCwKShE,14971
84
84
  jarvis/jarvis_stats/__init__.py,sha256=jJzgP43nxzLbNGs8Do4Jfta1PNCJMf1Oq9YTPd6EnFM,342
85
85
  jarvis/jarvis_stats/cli.py,sha256=iWuhA1AQAIOccXVUaO-k87T2b10s08wyeKAY5lUQ4og,12702
86
86
  jarvis/jarvis_stats/stats.py,sha256=AiQck1ma78YGR4hjT-leab-6GRMIQQmCp7wcG5SRKWw,19379
@@ -90,8 +90,8 @@ jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
90
90
  jarvis/jarvis_tools/ask_user.py,sha256=M6DdLNryCE8y1JcdZHEifUgZkPUEPNKc-zDW5p0Mb1k,2029
91
91
  jarvis/jarvis_tools/base.py,sha256=tFZkRlbV_a-pbjM-ci9AYmXVJm__FXuzVWKbQEyz4Ao,1639
92
92
  jarvis/jarvis_tools/clear_memory.py,sha256=_GlwqlCAsoHeB24Y1CnjLdMawRTc6cq55AA8Yi5AZg4,8249
93
- jarvis/jarvis_tools/edit_file.py,sha256=BUz0kfv44kqfNDr3tZKS-7Cn5DDhYl8xhHO7X8x7HQI,7086
94
- jarvis/jarvis_tools/execute_script.py,sha256=kASNTShHVGlHm7pZZxUeyEZHzHAYiZ-87AzrYVyORMw,6231
93
+ jarvis/jarvis_tools/edit_file.py,sha256=4Q0-ofyUVd3R5TAoQqQfTZCBlE5fgvgD3bf7_6wiZ3I,7139
94
+ jarvis/jarvis_tools/execute_script.py,sha256=UeOTv5Vf6KqmPH-gSwFf2xz4pRAADo9KTUUpi6yU138,6221
95
95
  jarvis/jarvis_tools/file_analyzer.py,sha256=jzVb8fAJn3dWwpCiYH-Wuxva4kpHqBB2_V3x3mzY0Gs,4158
96
96
  jarvis/jarvis_tools/generate_new_tool.py,sha256=OCHkBOCQflgMZXRP4vgX3blnJOUq-_QkcBFRQFj3sj0,7894
97
97
  jarvis/jarvis_tools/methodology.py,sha256=_K4GIDUodGEma3SvNRo7Qs5rliijgNespVLyAPN35JU,5233
@@ -102,28 +102,28 @@ jarvis/jarvis_tools/retrieve_memory.py,sha256=hQ3s4IgBHtmUPzuWFl5Pc2FLEuT2gdi4l4
102
102
  jarvis/jarvis_tools/rewrite_file.py,sha256=CuvjWPTbUaPbex9FKSmw_Ru4r6R-CX_3vqTqCTp8nHA,6959
103
103
  jarvis/jarvis_tools/save_memory.py,sha256=QKj6iYZL2XxPX0NnU9HqvoDOpJZ38mJmatDmHLK2r74,7012
104
104
  jarvis/jarvis_tools/search_web.py,sha256=8ugOcGBsAEg59pX_WS0WADuu7Klw7XGR6guEMz4OnwI,6181
105
- jarvis/jarvis_tools/sub_agent.py,sha256=y9SuuFBVPpQQxS0SXO4T1125YG8zfwKuBXLlHiVPrF8,8084
106
- jarvis/jarvis_tools/sub_code_agent.py,sha256=9YEYK8hbciGhvC44MU3Y5vQc4hYgOAiahiUbUK_w2iI,7696
105
+ jarvis/jarvis_tools/sub_agent.py,sha256=GTn6JXIsomHw9M46HTybIYgyFHUc72AFrbI4l9cwUro,7983
106
+ jarvis/jarvis_tools/sub_code_agent.py,sha256=JSQKJNBO3zQsCNk3x-CPzqTIRPeOt8rvP4WPC-JQOwI,7848
107
107
  jarvis/jarvis_tools/virtual_tty.py,sha256=1AYrVNP5QCX1HGo-xi3XEP93cMWZUOkil4ki2gwf7dI,25504
108
108
  jarvis/jarvis_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
109
  jarvis/jarvis_tools/cli/main.py,sha256=pyvSz0-hBduOqQShduv76UcB5wrK0v6MLonpub9AuXg,9334
110
110
  jarvis/jarvis_utils/__init__.py,sha256=67h0ldisGlh3oK4DAeNEL2Bl_VsI3tSmfclasyVlueM,850
111
111
  jarvis/jarvis_utils/builtin_replace_map.py,sha256=4BurljGuiG_I93EBs7mlFlPm9wYC_4CmdTG5tQWpF6g,1712
112
112
  jarvis/jarvis_utils/clipboard.py,sha256=D3wzQeqg_yiH7Axs4d6MRxyNa9XxdnenH-ND2uj2WVQ,2967
113
- jarvis/jarvis_utils/config.py,sha256=6wWqByse15Nsi5ciWvaqlC0AfxvK-7DXy95sUCyraKQ,18427
113
+ jarvis/jarvis_utils/config.py,sha256=44MIH-CLRYpv8ltpirnq5_EccE1fVVGQciCiWRuDYmo,18824
114
114
  jarvis/jarvis_utils/embedding.py,sha256=oEOEM2qf16DMYwPsQe6srET9BknyjOdY2ef0jsp3Or8,2714
115
115
  jarvis/jarvis_utils/file_processors.py,sha256=XiM248SHS7lLgQDCbORVFWqinbVDUawYxWDOsLXDxP8,3043
116
- jarvis/jarvis_utils/git_utils.py,sha256=AkczUiRcGcOnPfz2v3mdLwV1S41IopiAYD2tjeMTDrE,23586
116
+ jarvis/jarvis_utils/git_utils.py,sha256=X-j4aopmzmAW8Uv5rs3SDRnsAQYwJRnR6gR8yxXGEWg,23500
117
117
  jarvis/jarvis_utils/globals.py,sha256=aTrOHcCgPAeZFLFIWMAMiJCYlmr4XhdFZf5gZ745hnE,8900
118
118
  jarvis/jarvis_utils/http.py,sha256=eRhV3-GYuWmQ0ogq9di9WMlQkFcVb1zGCrySnOgT1x0,4392
119
- jarvis/jarvis_utils/input.py,sha256=iLY6kRm-kjKpsDyQQM49ppnXCZs_6jZVqLbvUWglrls,36657
119
+ jarvis/jarvis_utils/input.py,sha256=tGIr3-t46gT3dD-Z6Bcb5xagIifM62cAHRkk3w9p7_Q,36635
120
120
  jarvis/jarvis_utils/methodology.py,sha256=3PAMHVF85IflBBblV_mMwkYXMelUML0nZYZ8MOAYUJ8,12918
121
121
  jarvis/jarvis_utils/output.py,sha256=svCHLuXHe3Ta_qfoT5DxO80nyYyMfBFIKgziQS__Nmg,13632
122
122
  jarvis/jarvis_utils/tag.py,sha256=f211opbbbTcSyzCDwuIK_oCnKhXPNK-RknYyGzY1yD0,431
123
- jarvis/jarvis_utils/utils.py,sha256=KdNXmXkwZZZwfjoFT-UivjGBBaB3feFNUHQ13U_HApo,62210
124
- jarvis_ai_assistant-0.3.24.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
125
- jarvis_ai_assistant-0.3.24.dist-info/METADATA,sha256=g1hsQNJc_yGSieqNhztNwmB7KQR6YjCEoqp2c8icNww,18790
126
- jarvis_ai_assistant-0.3.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
127
- jarvis_ai_assistant-0.3.24.dist-info/entry_points.txt,sha256=4GcWKFxRJD-QU14gw_3ZaW4KuEVxOcZK9i270rwPdjA,1395
128
- jarvis_ai_assistant-0.3.24.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
129
- jarvis_ai_assistant-0.3.24.dist-info/RECORD,,
123
+ jarvis/jarvis_utils/utils.py,sha256=npqPuSVqCYFmcrIK_J-MqmMlRxZ9oZDbps9bT_tNI-E,63205
124
+ jarvis_ai_assistant-0.3.26.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
125
+ jarvis_ai_assistant-0.3.26.dist-info/METADATA,sha256=AFxKVvM2rsHE0Y18QCxQ9bddXdJKcmk0t2S1NWKaXw4,18790
126
+ jarvis_ai_assistant-0.3.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
127
+ jarvis_ai_assistant-0.3.26.dist-info/entry_points.txt,sha256=4GcWKFxRJD-QU14gw_3ZaW4KuEVxOcZK9i270rwPdjA,1395
128
+ jarvis_ai_assistant-0.3.26.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
129
+ jarvis_ai_assistant-0.3.26.dist-info/RECORD,,