jarvis-ai-assistant 0.1.174__py3-none-any.whl → 0.1.175__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.

Potentially problematic release.


This version of jarvis-ai-assistant might be problematic. Click here for more details.

jarvis/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.1.174"
4
+ __version__ = "0.1.175"
@@ -11,7 +11,7 @@ from jarvis.jarvis_platform.registry import PlatformRegistry
11
11
  from jarvis.jarvis_utils.output import PrettyOutput, OutputType
12
12
  from jarvis.jarvis_utils.embedding import get_context_token_count
13
13
  from jarvis.jarvis_utils.config import get_max_tool_call_count, is_auto_complete, is_execute_tool_confirm
14
- from jarvis.jarvis_utils.methodology import load_methodology
14
+ from jarvis.jarvis_utils.methodology import load_methodology, upload_methodology
15
15
  from jarvis.jarvis_utils.globals import make_agent_name, set_agent, delete_agent
16
16
  from jarvis.jarvis_utils.input import get_multiline_input
17
17
  from jarvis.jarvis_utils.config import get_max_token_count
@@ -645,7 +645,7 @@ want: 创建新工具来解决XXX问题
645
645
  name: generate_new_tool
646
646
  arguments:
647
647
  tool_name: 工具名称
648
- tool_code: |
648
+ tool_code: |2
649
649
  # -*- coding: utf-8 -*-
650
650
  from typing import Dict, Any
651
651
  from jarvis.jarvis_utils.output import PrettyOutput, OutputType
@@ -699,96 +699,12 @@ name: methodology
699
699
  arguments:
700
700
  operation: add/update
701
701
  problem_type: 方法论类型,不要过于细节,也不要过于泛化
702
- content: |
702
+ content: |2
703
703
  方法论内容
704
704
  {ct("TOOL_CALL")}
705
705
 
706
706
  如果以上三种情况都不适用,则直接输出原因分析,不要使用工具调用格式。
707
707
  </output_requirements>
708
-
709
- <tool_example>
710
- 以下是一个完整的工具示例,供参考:
711
-
712
- ```python
713
- # -*- coding: utf-8 -*-
714
- from typing import Dict, Any
715
- from jarvis.jarvis_utils.output import PrettyOutput, OutputType
716
-
717
- class text_transformer:
718
- name = "text_transformer"
719
- description = "Tool for text transformation"
720
- Tool description for text transformation
721
- 适用场景:1. 格式化文本; 2. 处理标题; 3. 标准化输出
722
- \"\"\"
723
-
724
- parameters = {{
725
- "type": "object",
726
- "properties": {{
727
- "text": {{
728
- "type": "string",
729
- "description": "需要转换格式的文本"
730
- }},
731
- "transform_type": {{
732
- "type": "string",
733
- "description": "转换类型,可选值为 upper(大写)、lower(小写)或 title(首字母大写)",
734
- "enum": ["upper", "lower", "title"]
735
- }}
736
- }},
737
- "required": ["text", "transform_type"]
738
- }}
739
-
740
- @staticmethod
741
- def check() -> bool:
742
- return True
743
-
744
- def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
745
- try:
746
- text = args["text"]
747
- transform_type = args["transform_type"]
748
-
749
- # 使用PrettyOutput显示执行过程
750
- PrettyOutput.print(f"正在将文本转换为 {{transform_type}} 格式...", OutputType.INFO)
751
-
752
- if transform_type == "upper":
753
- result = text.upper()
754
- PrettyOutput.print("文本已转换为大写", OutputType.SUCCESS)
755
- elif transform_type == "lower":
756
- result = text.lower()
757
- PrettyOutput.print("文本已转换为小写", OutputType.SUCCESS)
758
- elif transform_type == "title":
759
- result = text.title()
760
- PrettyOutput.print("文本已转换为首字母大写", OutputType.SUCCESS)
761
- else:
762
- PrettyOutput.print(f"不支持的转换类型: {{transform_type}}", OutputType.ERROR)
763
- return {{
764
- "success": False,
765
- "stdout": "",
766
- "stderr": f"不支持的转换类型: {{transform_type}}"
767
- }}
768
-
769
- return {{
770
- "success": True,
771
- "stdout": result,
772
- "stderr": ""
773
- }}
774
-
775
- except Exception as e:
776
- PrettyOutput.print(f"转换失败: {{str(e)}}", OutputType.ERROR)
777
- return {{
778
- "success": False,
779
- "stdout": "",
780
- "stderr": f"转换失败: {{str(e)}}"
781
- }}
782
- ```
783
-
784
- 使用方法:
785
- ```
786
- name: text_transformer
787
- arguments:
788
- text: hello world
789
- transform_type: upper
790
- ```
791
- </tool_example>
792
708
  </task_analysis>"""
793
709
 
794
710
  self.prompt = analysis_prompt
@@ -838,7 +754,16 @@ arguments:
838
754
  msg = user_input
839
755
  for handler in self.input_handler:
840
756
  msg, _ = handler(msg, self)
841
- self.prompt = f"{user_input}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
757
+
758
+ # 先尝试上传方法轮
759
+ platform = self.model if hasattr(self.model, 'upload_files') else None
760
+ if platform and upload_methodology(platform):
761
+ methodology_prompt = f"{user_input}\n\n方法论已上传到平台,请参考平台上的方法论内容"
762
+ else:
763
+ # 上传失败则回退到本地加载
764
+ methodology_prompt = f"{user_input}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
765
+
766
+ self.prompt = methodology_prompt
842
767
  self.first = False
843
768
 
844
769
  self.conversation_length = get_context_token_count(self.prompt)
@@ -374,7 +374,8 @@ class CodeAgent:
374
374
  final_ret += f"# 应用补丁:\n```diff\n{diff}\n```"
375
375
 
376
376
  # 修改后的提示逻辑
377
- addon_prompt = f"如果用户的需求未完成,请继续生成补丁,如果已经完成,请终止,不要输出新的PATCH,不要实现任何超出用户需求外的内容\n"
377
+ addon_prompt = "如果对应语言有静态检查工具,请使用静态检查工具检查代码,如果本次修改引入了警告和错误,请根据警告和错误信息修复代码\n"
378
+ addon_prompt += "在引入警告和错误都被修复的前提下,如果用户的需求未完成,请继续生成补丁,如果已经完成,请终止,不要实现任何超出用户需求外的内容\n"
378
379
  addon_prompt += "如果有任何信息不明确,调用工具获取信息\n"
379
380
  addon_prompt += "每次响应必须且只能包含一个操作\n"
380
381
 
jarvis/jarvis_dev/main.py CHANGED
@@ -125,7 +125,7 @@ PM_PROMPT = f"""
125
125
  ## 消息传递模板
126
126
  {ot("SEND_MESSAGE")}
127
127
  to: [角色]
128
- content: |
128
+ content: |2
129
129
  # [任务主题]
130
130
 
131
131
  ## 背景与目标
@@ -259,7 +259,7 @@ BA_PROMPT = f"""
259
259
  ## 消息传递模板
260
260
  {ot("SEND_MESSAGE")}
261
261
  to: [角色]
262
- content: |
262
+ content: |2
263
263
  # [需求主题]
264
264
 
265
265
  ## 背景与目标
@@ -415,7 +415,7 @@ SA_PROMPT = f"""
415
415
  ## 消息传递模板
416
416
  {ot("SEND_MESSAGE")}
417
417
  to: [角色]
418
- content: |
418
+ content: |2
419
419
  # [架构主题]
420
420
 
421
421
  ## 背景与目标
@@ -571,7 +571,7 @@ TL_PROMPT = f"""
571
571
  ## 消息传递模板
572
572
  {ot("SEND_MESSAGE")}
573
573
  to: [角色]
574
- content: |
574
+ content: |2
575
575
  # [技术主题]
576
576
 
577
577
  ## 背景与目标
@@ -750,7 +750,7 @@ arguments:
750
750
  ## 消息传递模板
751
751
  {ot("SEND_MESSAGE")}
752
752
  to: [角色]
753
- content: |
753
+ content: |2
754
754
  # [开发主题]
755
755
 
756
756
  ## 背景与目标
@@ -931,7 +931,7 @@ arguments:
931
931
  ## 消息传递模板
932
932
  {ot("SEND_MESSAGE")}
933
933
  to: [角色]
934
- content: |
934
+ content: |2
935
935
  # [测试主题]
936
936
 
937
937
  ## 背景与目标
@@ -95,10 +95,10 @@ def extract_methodology(input_file):
95
95
  请按以下格式返回结果:
96
96
  <methodologies>
97
97
  - problem_type: [问题类型1]
98
- content: |
98
+ content: |2
99
99
  [多行方法论内容1]
100
100
  - problem_type: [问题类型2]
101
- content: |
101
+ content: |2
102
102
  [多行方法论内容2]
103
103
  </methodologies>
104
104
 
@@ -192,10 +192,10 @@ def extract_methodology_from_url(url):
192
192
  请按以下格式返回结果:
193
193
  <methodologies>
194
194
  - problem_type: [问题类型1]
195
- content: |
195
+ content: |2
196
196
  [多行方法论内容1]
197
197
  - problem_type: [问题类型2]
198
- content: |
198
+ content: |2
199
199
  [多行方法论内容2]
200
200
  </methodologies>
201
201
 
@@ -33,23 +33,18 @@ class MultiAgent(OutputHandler):
33
33
  ```
34
34
  {ot("SEND_MESSAGE")}
35
35
  to: 智能体名称 # 目标智能体名称
36
- content: |
37
- # 消息主题
38
-
39
- ## 背景信息
40
- [提供必要的上下文和背景]
41
-
42
- ## 具体需求
43
- [明确表达期望完成的任务]
44
-
45
- ## 相关资源
46
- [列出相关文档、数据或工具]
47
-
48
- ## 期望结果
49
- [描述期望的输出格式和内容]
50
-
51
- ## 下一步计划
52
- [描述下一步的计划和行动]
36
+ content: |2
37
+ # 消息主题
38
+ ## 背景信息
39
+ [提供必要的上下文和背景]
40
+ ## 具体需求
41
+ [明确表达期望完成的任务]
42
+ ## 相关资源
43
+ [列出相关文档、数据或工具]
44
+ ## 期望结果
45
+ [描述期望的输出格式和内容]
46
+ ## 下一步计划
47
+ [描述下一步的计划和行动]
53
48
  {ct("SEND_MESSAGE")}
54
49
  ```
55
50
 
@@ -58,11 +53,10 @@ content: |
58
53
  ```
59
54
  {ot("SEND_MESSAGE")}
60
55
  to: 智能体名称 # 目标智能体名称
61
- content: |
62
- # 消息主题
63
-
64
- ## 任务结果
65
- [任务完成结果,用于反馈]
56
+ content: |2
57
+ # 消息主题
58
+ ## 任务结果
59
+ [任务完成结果,用于反馈]
66
60
  {ct("SEND_MESSAGE")}
67
61
  ```
68
62
 
@@ -5,6 +5,10 @@ import json
5
5
  import os
6
6
  import mimetypes
7
7
  import time
8
+ from rich.live import Live
9
+ from rich.text import Text
10
+ from rich.panel import Panel
11
+ from rich import box
8
12
  from jarvis.jarvis_platform.base import BasePlatform
9
13
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
10
14
  from jarvis.jarvis_utils.utils import while_success
@@ -281,61 +285,127 @@ class KimiModel(BasePlatform):
281
285
  search_results = []
282
286
  ref_sources = []
283
287
 
284
- for line in response.iter_lines():
285
- if not line:
286
- continue
287
-
288
- line = line.decode('utf-8')
289
- if not line.startswith("data: "):
290
- continue
291
-
292
- try:
293
- data = json.loads(line[6:])
294
- event = data.get("event")
295
-
296
- if event == "cmpl":
297
- # 处理补全文本
298
- text = data.get("text", "")
299
- if text:
300
- if not self.suppress_output:
301
- PrettyOutput.print_stream(text)
302
- full_response += text
303
-
304
- elif event == "search_plus":
305
- # 收集搜索结果
306
- msg = data.get("msg", {})
307
- if msg.get("type") == "get_res":
308
- search_results.append({
309
- "date": msg.get("date", ""),
310
- "site_name": msg.get("site_name", ""),
311
- "snippet": msg.get("snippet", ""),
312
- "title": msg.get("title", ""),
313
- "type": msg.get("type", ""),
314
- "url": msg.get("url", "")
315
- })
316
-
317
- elif event == "ref_docs":
318
- # 收集引用来源
319
- ref_cards = data.get("ref_cards", [])
320
- for card in ref_cards:
321
- ref_sources.append({
322
- "idx_s": card.get("idx_s", ""),
323
- "idx_z": card.get("idx_z", ""),
324
- "ref_id": card.get("ref_id", ""),
325
- "url": card.get("url", ""),
326
- "title": card.get("title", ""),
327
- "abstract": card.get("abstract", ""),
328
- "source": card.get("source_label", ""),
329
- "rag_segments": card.get("rag_segments", []),
330
- "origin": card.get("origin", {})
331
- })
332
-
333
- except json.JSONDecodeError:
334
- continue
335
-
288
+ # 使用Rich的Live组件来实时展示更新
336
289
  if not self.suppress_output:
337
- PrettyOutput.print_stream_end()
338
-
290
+ text_content = Text()
291
+ panel = Panel(text_content,
292
+ title=f"[bold magenta]{self.model_name}[/bold magenta]",
293
+ subtitle="思考中...",
294
+ border_style="magenta",
295
+ box=box.ROUNDED)
296
+
297
+ with Live(panel, refresh_per_second=3, transient=False) as live:
298
+ for line in response.iter_lines():
299
+ if not line:
300
+ continue
301
+
302
+ line = line.decode('utf-8')
303
+ if not line.startswith("data: "):
304
+ continue
305
+
306
+ try:
307
+ data = json.loads(line[6:])
308
+ event = data.get("event")
309
+
310
+ if event == "cmpl":
311
+ # 处理补全文本
312
+ text = data.get("text", "")
313
+ if text:
314
+ full_response += text
315
+ text_content.append(text)
316
+ panel.subtitle = "生成中..."
317
+ live.update(panel)
318
+
319
+ elif event == "search_plus":
320
+ # 收集搜索结果
321
+ msg = data.get("msg", {})
322
+ if msg.get("type") == "get_res":
323
+ search_results.append({
324
+ "date": msg.get("date", ""),
325
+ "site_name": msg.get("site_name", ""),
326
+ "snippet": msg.get("snippet", ""),
327
+ "title": msg.get("title", ""),
328
+ "type": msg.get("type", ""),
329
+ "url": msg.get("url", "")
330
+ })
331
+ panel.subtitle = f"搜索中: 找到 {len(search_results)} 个结果"
332
+ live.update(panel)
333
+
334
+ elif event == "ref_docs":
335
+ # 收集引用来源
336
+ ref_cards = data.get("ref_cards", [])
337
+ for card in ref_cards:
338
+ ref_sources.append({
339
+ "idx_s": card.get("idx_s", ""),
340
+ "idx_z": card.get("idx_z", ""),
341
+ "ref_id": card.get("ref_id", ""),
342
+ "url": card.get("url", ""),
343
+ "title": card.get("title", ""),
344
+ "abstract": card.get("abstract", ""),
345
+ "source": card.get("source_label", ""),
346
+ "rag_segments": card.get("rag_segments", []),
347
+ "origin": card.get("origin", {})
348
+ })
349
+ panel.subtitle = f"分析引用: 找到 {len(ref_sources)} 个来源"
350
+ live.update(panel)
351
+
352
+ except json.JSONDecodeError:
353
+ continue
354
+
355
+ # 显示对话完成状态
356
+ panel.subtitle = "[bold green]回答完成[/bold green]"
357
+ live.update(panel)
358
+ else:
359
+ # 如果禁止输出,则静默处理
360
+ for line in response.iter_lines():
361
+ if not line:
362
+ continue
363
+
364
+ line = line.decode('utf-8')
365
+ if not line.startswith("data: "):
366
+ continue
367
+
368
+ try:
369
+ data = json.loads(line[6:])
370
+ event = data.get("event")
371
+
372
+ if event == "cmpl":
373
+ # 处理补全文本
374
+ text = data.get("text", "")
375
+ if text:
376
+ full_response += text
377
+
378
+ elif event == "search_plus":
379
+ # 收集搜索结果
380
+ msg = data.get("msg", {})
381
+ if msg.get("type") == "get_res":
382
+ search_results.append({
383
+ "date": msg.get("date", ""),
384
+ "site_name": msg.get("site_name", ""),
385
+ "snippet": msg.get("snippet", ""),
386
+ "title": msg.get("title", ""),
387
+ "type": msg.get("type", ""),
388
+ "url": msg.get("url", "")
389
+ })
390
+
391
+ elif event == "ref_docs":
392
+ # 收集引用来源
393
+ ref_cards = data.get("ref_cards", [])
394
+ for card in ref_cards:
395
+ ref_sources.append({
396
+ "idx_s": card.get("idx_s", ""),
397
+ "idx_z": card.get("idx_z", ""),
398
+ "ref_id": card.get("ref_id", ""),
399
+ "url": card.get("url", ""),
400
+ "title": card.get("title", ""),
401
+ "abstract": card.get("abstract", ""),
402
+ "source": card.get("source_label", ""),
403
+ "rag_segments": card.get("rag_segments", []),
404
+ "origin": card.get("origin", {})
405
+ })
406
+
407
+ except json.JSONDecodeError:
408
+ continue
339
409
 
340
410
  # 显示搜索结果摘要
341
411
  if search_results and not self.suppress_output:
@@ -2,6 +2,10 @@
2
2
  from typing import Dict, List, Tuple
3
3
  import os
4
4
  from openai import OpenAI
5
+ from rich.live import Live
6
+ from rich.text import Text
7
+ from rich.panel import Panel
8
+ from rich import box
5
9
  from jarvis.jarvis_platform.base import BasePlatform
6
10
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
7
11
 
@@ -84,15 +88,32 @@ class OpenAIModel(BasePlatform):
84
88
 
85
89
  full_response = ""
86
90
 
87
- for chunk in response:
88
- if chunk.choices and chunk.choices[0].delta.content:
89
- text = chunk.choices[0].delta.content
90
- if not self.suppress_output:
91
- PrettyOutput.print_stream(text)
92
- full_response += text
93
-
91
+ # 使用Rich的Live组件来实时展示更新
94
92
  if not self.suppress_output:
95
- PrettyOutput.print_stream_end()
93
+ text_content = Text()
94
+ panel = Panel(text_content,
95
+ title=f"[bold blue]{self.model_name}[/bold blue]",
96
+ subtitle="生成中...",
97
+ border_style="cyan",
98
+ box=box.ROUNDED)
99
+
100
+ with Live(panel, refresh_per_second=3, transient=False) as live:
101
+ for chunk in response:
102
+ if chunk.choices and chunk.choices[0].delta.content:
103
+ text = chunk.choices[0].delta.content
104
+ full_response += text
105
+ text_content.append(text)
106
+ live.update(panel)
107
+
108
+ # 显示对话完成状态
109
+ panel.subtitle = "[bold green]对话完成[/bold green]"
110
+ live.update(panel)
111
+ else:
112
+ # 如果禁止输出,则静默处理
113
+ for chunk in response:
114
+ if chunk.choices and chunk.choices[0].delta.content:
115
+ text = chunk.choices[0].delta.content
116
+ full_response += text
96
117
 
97
118
  # Add assistant reply to history
98
119
  self.messages.append({"role": "assistant", "content": full_response})
@@ -11,6 +11,10 @@ from PIL import Image
11
11
  from yaspin import yaspin
12
12
  from yaspin.spinners import Spinners
13
13
  from yaspin.api import Yaspin
14
+ from rich.live import Live
15
+ from rich.text import Text
16
+ from rich.panel import Panel
17
+ from rich import box
14
18
  from jarvis.jarvis_platform.base import BasePlatform
15
19
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
16
20
  from jarvis.jarvis_utils.utils import while_success
@@ -387,7 +391,6 @@ class YuanbaoPlatform(BasePlatform):
387
391
 
388
392
  参数:
389
393
  message: 要发送的消息文本
390
- file_list: 可选的上传和附加文件路径列表
391
394
 
392
395
  返回:
393
396
  模型的响应
@@ -425,7 +428,9 @@ class YuanbaoPlatform(BasePlatform):
425
428
  self.multimedia = []
426
429
 
427
430
  if self.web:
428
- payload["supportFunctions"] = ["supportInternetSearch"]
431
+ payload["supportFunctions"] = ["openInternetSearch"]
432
+ else:
433
+ payload["supportFunctions"] = ["autoInternetSearch"]
429
434
 
430
435
  # 添加系统消息(如果是第一次对话)
431
436
  if self.first_chat and self.system_message:
@@ -449,45 +454,82 @@ class YuanbaoPlatform(BasePlatform):
449
454
 
450
455
  full_response = ""
451
456
  is_text_block = False
457
+ thinking_content = ""
452
458
 
453
- # 处理SSE流响应
454
- for line in response.iter_lines():
455
- if not line:
456
- continue
457
-
458
- line_str = line.decode('utf-8')
459
-
460
- # SSE格式的行通常以"data: "开头
461
- if line_str.startswith("data: "):
462
- try:
463
- data_str = line_str[6:] # 移除"data: "前缀
464
- data = json.loads(data_str)
465
-
466
- # 处理文本类型的消息
467
- if data.get("type") == "text":
468
- is_text_block = True
469
- msg = data.get("msg", "")
470
- if msg:
471
- if not self.suppress_output:
472
- PrettyOutput.print_stream(msg)
473
- full_response += msg
474
-
475
- # 处理思考中的消息(可选展示)
476
- elif data.get("type") == "think" and not self.suppress_output:
477
- think_content = data.get("content", "")
478
- # 可以选择性地显示思考过程,但不加入最终响应
479
- PrettyOutput.print_stream(f"{think_content}", is_thinking=True)
480
- pass
459
+ # 使用Rich的Live组件来实时展示更新
460
+ if not self.suppress_output:
461
+ text_content = Text()
462
+ panel = Panel(text_content, title=f"[bold blue]{self.model_name}[/bold blue]",
463
+ subtitle="思考中...", border_style="blue", box=box.ROUNDED)
464
+ with Live(panel, refresh_per_second=3, transient=False) as live:
465
+ # 处理SSE流响应
466
+ for line in response.iter_lines():
467
+ if not line:
468
+ continue
469
+
470
+ line_str = line.decode('utf-8')
471
+
472
+ # SSE格式的行通常以"data: "开头
473
+ if line_str.startswith("data: "):
474
+ try:
475
+ data_str = line_str[6:] # 移除"data: "前缀
476
+ data = json.loads(data_str)
477
+
478
+ # 处理文本类型的消息
479
+ if data.get("type") == "text":
480
+ is_text_block = True
481
+ msg = data.get("msg", "")
482
+ if msg:
483
+ full_response += msg
484
+ text_content.append(msg)
485
+ panel.subtitle = "正在回答..."
486
+ live.update(panel)
487
+
488
+ # 处理思考中的消息
489
+ elif data.get("type") == "think":
490
+ think_content = data.get("content", "")
491
+ if think_content:
492
+ thinking_content = think_content
493
+ panel.subtitle = f"思考中: {thinking_content}"
494
+ live.update(panel)
495
+
496
+ except json.JSONDecodeError:
497
+ pass
498
+
499
+ # 检测结束标志
500
+ elif line_str == "data: [DONE]":
501
+ break
502
+
503
+ # 显示对话完成状态
504
+ panel.subtitle = "[bold green]对话完成[/bold green]"
505
+ live.update(panel)
506
+ else:
507
+ # 如果禁止输出,则静默处理
508
+ for line in response.iter_lines():
509
+ if not line:
510
+ continue
481
511
 
482
- except json.JSONDecodeError:
483
- pass
512
+ line_str = line.decode('utf-8')
484
513
 
485
- # 检测结束标志
486
- elif line_str == "data: [DONE]":
487
- break
514
+ # SSE格式的行通常以"data: "开头
515
+ if line_str.startswith("data: "):
516
+ try:
517
+ data_str = line_str[6:] # 移除"data: "前缀
518
+ data = json.loads(data_str)
488
519
 
489
- if not self.suppress_output:
490
- PrettyOutput.print_stream_end()
520
+ # 处理文本类型的消息
521
+ if data.get("type") == "text":
522
+ is_text_block = True
523
+ msg = data.get("msg", "")
524
+ if msg:
525
+ full_response += msg
526
+
527
+ except json.JSONDecodeError:
528
+ pass
529
+
530
+ # 检测结束标志
531
+ elif line_str == "data: [DONE]":
532
+ break
491
533
 
492
534
  return full_response
493
535
 
@@ -51,6 +51,7 @@ def list_platforms():
51
51
  def chat_with_model(platform_name: str, model_name: str):
52
52
  """Chat with specified platform and model"""
53
53
  registry = PlatformRegistry.get_global_platform_registry()
54
+ conversation_history = [] # 存储对话记录
54
55
 
55
56
  # Create platform instance
56
57
  platform = registry.create_platform(platform_name)
@@ -63,12 +64,13 @@ def chat_with_model(platform_name: str, model_name: str):
63
64
  platform.set_model_name(model_name)
64
65
  platform.set_suppress_output(False)
65
66
  PrettyOutput.print(f"连接到 {platform_name} 平台 {model_name} 模型", OutputType.SUCCESS)
66
- PrettyOutput.print("可用命令: /bye - 退出聊天, /clear - 清除会话, /upload - 上传文件, /shell - 执行shell命令", OutputType.INFO)
67
+ PrettyOutput.print("可用命令: /bye - 退出聊天, /clear - 清除会话, /upload - 上传文件, /shell - 执行shell命令, /save - 保存当前对话, /saveall - 保存所有对话", OutputType.INFO)
67
68
 
68
69
  # Start conversation loop
69
70
  while True:
70
71
  # Get user input
71
72
  user_input = get_multiline_input("")
73
+ conversation_history.append({"role": "user", "content": user_input}) # 记录用户输入
72
74
 
73
75
  # Check if input is cancelled
74
76
  if user_input.strip() == "/bye":
@@ -84,6 +86,7 @@ def chat_with_model(platform_name: str, model_name: str):
84
86
  try:
85
87
  platform.reset()
86
88
  platform.set_model_name(model_name) # Reinitialize session
89
+ conversation_history = [] # 重置对话记录
87
90
  PrettyOutput.print("会话已清除", OutputType.SUCCESS)
88
91
  except Exception as e:
89
92
  PrettyOutput.print(f"清除会话失败: {str(e)}", OutputType.ERROR)
@@ -110,6 +113,52 @@ def chat_with_model(platform_name: str, model_name: str):
110
113
  PrettyOutput.print(f"上传文件失败: {str(e)}", OutputType.ERROR)
111
114
  continue
112
115
 
116
+ # Check if it is a save command
117
+ if user_input.strip().startswith("/save"):
118
+ try:
119
+ file_path = user_input.strip()[5:].strip()
120
+ if not file_path:
121
+ PrettyOutput.print("请指定保存文件名,例如: /save last_message.txt", OutputType.WARNING)
122
+ continue
123
+
124
+ # Remove quotes if present
125
+ if (file_path.startswith('"') and file_path.endswith('"')) or (file_path.startswith("'") and file_path.endswith("'")):
126
+ file_path = file_path[1:-1]
127
+
128
+ # Write last message content to file
129
+ if conversation_history:
130
+ with open(file_path, 'w', encoding='utf-8') as f:
131
+ last_entry = conversation_history[-1]
132
+ f.write(f"{last_entry['content']}\n")
133
+ PrettyOutput.print(f"最后一条消息内容已保存到 {file_path}", OutputType.SUCCESS)
134
+ else:
135
+ PrettyOutput.print("没有可保存的消息", OutputType.WARNING)
136
+ except Exception as e:
137
+ PrettyOutput.print(f"保存消息失败: {str(e)}", OutputType.ERROR)
138
+ continue
139
+
140
+ # Check if it is a saveall command
141
+ if user_input.strip().startswith("/saveall"):
142
+ try:
143
+ file_path = user_input.strip()[8:].strip()
144
+ if not file_path:
145
+ PrettyOutput.print("请指定保存文件名,例如: /saveall all_conversations.txt", OutputType.WARNING)
146
+ continue
147
+
148
+ # Remove quotes if present
149
+ if (file_path.startswith('"') and file_path.endswith('"')) or (file_path.startswith("'") and file_path.endswith("'")):
150
+ file_path = file_path[1:-1]
151
+
152
+ # Write full conversation history to file
153
+ with open(file_path, 'w', encoding='utf-8') as f:
154
+ for entry in conversation_history:
155
+ f.write(f"{entry['role']}: {entry['content']}\n\n")
156
+
157
+ PrettyOutput.print(f"所有对话已保存到 {file_path}", OutputType.SUCCESS)
158
+ except Exception as e:
159
+ PrettyOutput.print(f"保存所有对话失败: {str(e)}", OutputType.ERROR)
160
+ continue
161
+
113
162
  # Check if it is a shell command
114
163
  if user_input.strip().startswith("/shell"):
115
164
  try:
@@ -133,6 +182,8 @@ def chat_with_model(platform_name: str, model_name: str):
133
182
  response = platform.chat_until_success(user_input)
134
183
  if not response:
135
184
  PrettyOutput.print("没有有效的回复", OutputType.WARNING)
185
+ else:
186
+ conversation_history.append({"role": "assistant", "content": response}) # 记录模型回复
136
187
 
137
188
  except Exception as e:
138
189
  PrettyOutput.print(f"聊天失败: {str(e)}", OutputType.ERROR)
@@ -33,8 +33,8 @@ tool_call_help = f"""
33
33
  want: 想要从执行结果中获取到的信息,如果工具输出内容过长,会根据此字段尝试提取有效信息
34
34
  name: 工具名称
35
35
  arguments:
36
- param1: 值1
37
- param2: 值2
36
+ param1: 值1
37
+ param2: 值2
38
38
  {ct("TOOL_CALL")}
39
39
  </format>
40
40
 
@@ -49,7 +49,7 @@ arguments:
49
49
  <rule>
50
50
  ### 2. 严格遵守格式
51
51
  - 完全按照上述格式
52
- - 使用正确的YAML缩进
52
+ - 使用正确的YAML格式,2个空格作为缩进
53
53
  - 包含所有必需参数
54
54
  </rule>
55
55
 
@@ -72,15 +72,15 @@ arguments:
72
72
 
73
73
  <string_format>
74
74
  # 📝 字符串参数格式
75
- 始终使用 | 语法表示字符串参数:
75
+ 始终使用 |2 语法表示字符串参数,防止多行字符串行首空格引起歧义:
76
76
 
77
77
  {ot("TOOL_CALL")}
78
78
  want: 当前的git状态,期望获取xxx的提交记录
79
79
  name: execute_script
80
80
  arguments:
81
- interpreter: bash
82
- script_cotent: |
83
- git status --porcelain
81
+ interpreter: bash
82
+ script_cotent: |2
83
+ git status --porcelain
84
84
  {ct("TOOL_CALL")}
85
85
  </string_format>
86
86
 
@@ -96,7 +96,7 @@ arguments:
96
96
  <common_errors>
97
97
  # ⚠️ 常见错误
98
98
  - 同时调用多个工具
99
- - 字符串参数缺少 |
99
+ - 字符串参数缺少 |2
100
100
  - 假设工具结果
101
101
  - 创建虚构对话
102
102
  - 在没有所需信息的情况下继续
@@ -12,6 +12,7 @@ import json
12
12
  import tempfile
13
13
  from typing import Any, Dict, Optional
14
14
 
15
+ from jarvis.jarvis_platform.base import BasePlatform
15
16
  from jarvis.jarvis_utils.config import get_data_dir
16
17
  from jarvis.jarvis_utils.output import PrettyOutput, OutputType
17
18
  from jarvis.jarvis_platform.registry import PlatformRegistry
@@ -92,6 +93,42 @@ def _create_methodology_temp_file(methodologies: Dict[str, str]) -> Optional[str
92
93
  PrettyOutput.print(f"创建方法论临时文件失败: {str(e)}", OutputType.ERROR)
93
94
  return None
94
95
 
96
+ def upload_methodology(platform: BasePlatform) -> bool:
97
+ """
98
+ 上传方法论文件到指定平台
99
+
100
+ 参数:
101
+ platform: 平台实例,需实现upload_files方法
102
+
103
+ 返回:
104
+ bool: 上传是否成功
105
+ """
106
+ methodology_dir = _get_methodology_directory()
107
+ if not os.path.exists(methodology_dir):
108
+ PrettyOutput.print("方法论文档不存在", OutputType.WARNING)
109
+ return False
110
+
111
+ methodologies = _load_all_methodologies()
112
+ if not methodologies:
113
+ PrettyOutput.print("没有可用的方法论文档", OutputType.WARNING)
114
+ return False
115
+
116
+ temp_file_path = _create_methodology_temp_file(methodologies)
117
+ if not temp_file_path:
118
+ return False
119
+
120
+ try:
121
+ if hasattr(platform, 'upload_files'):
122
+ return platform.upload_files([temp_file_path])
123
+ return False
124
+ finally:
125
+ if temp_file_path and os.path.exists(temp_file_path):
126
+ try:
127
+ os.remove(temp_file_path)
128
+ except Exception:
129
+ pass
130
+
131
+
95
132
  def load_methodology(user_input: str, tool_registery: Optional[Any] = None) -> str:
96
133
  """
97
134
  加载方法论并上传到大模型。
@@ -204,26 +204,7 @@ class PrettyOutput:
204
204
  console.print()
205
205
  console.print(panel)
206
206
  console.print()
207
- @staticmethod
208
- def print_stream(text: str, is_thinking: bool = False):
209
- """
210
- 打印流式输出,不带换行符。
211
-
212
- 参数:
213
- text: 要打印的文本
214
- """
215
- style = RichStyle(color="bright_cyan", bold=True, frame=True, meta={"icon": "🤖"})
216
- if is_thinking:
217
- style = RichStyle(color="grey58", italic=True, frame=True, meta={"icon": "🤖"})
218
- console.print(text, style=style, end="")
219
- @staticmethod
220
- def print_stream_end():
221
- """
222
- 结束流式输出,带换行符。
223
- """
224
- end_style = PrettyOutput._get_style(OutputType.SUCCESS)
225
- console.print("\n", style=end_style)
226
- console.file.flush()
207
+
227
208
  @staticmethod
228
209
  def _get_style(output_type: OutputType) -> RichStyle:
229
210
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.174
3
+ Version: 0.1.175
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,5 +1,5 @@
1
- jarvis/__init__.py,sha256=vUlw8YZKrztZai7bmOGjahPSihHH2wASeXbZvUZ1VHo,74
2
- jarvis/jarvis_agent/__init__.py,sha256=5N3W4AfkJ4nDxFjSDd70kQ0gI4b61-3VdGRds2SgE6Y,31484
1
+ jarvis/__init__.py,sha256=PW7wvclxKvhVfdxpCRbtFXcfiVyy6gIVpG9wmneeWDM,74
2
+ jarvis/jarvis_agent/__init__.py,sha256=O_Qx-pqZjpb8LaXIDywgdKDDg-6Lq07wBgMF4PQECfQ,29246
3
3
  jarvis/jarvis_agent/builtin_input_handler.py,sha256=KhvlV_QdB3P-M0TCkWvdxidNie1jU7KoMOqTIXCpwwA,1529
4
4
  jarvis/jarvis_agent/file_input_handler.py,sha256=cIiE0pwMJVe1VfeNw8Dpm8mM_gwTcre-nxLstFjoq4k,4152
5
5
  jarvis/jarvis_agent/jarvis.py,sha256=IuM-A9KZd1jqOFszB2l-Ua1FE5uVGvvNia1SSgp7Jgg,5963
@@ -7,7 +7,7 @@ jarvis/jarvis_agent/main.py,sha256=IAD59fEMWWSSAtHJhOQMPs_NMoKtcYjrxlTvhCHEVII,2
7
7
  jarvis/jarvis_agent/output_handler.py,sha256=7qori-RGrQmdiFepoEe3oPPKJIvRt90l_JDmvCoa4zA,1219
8
8
  jarvis/jarvis_agent/shell_input_handler.py,sha256=pi3AtPKrkKc6K9e99S1djKXQ_XrxtP6FrSWebQmRT6E,1261
9
9
  jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- jarvis/jarvis_code_agent/code_agent.py,sha256=vN-HZcwMnOfofq5JDjuVADQdBRdG1SuRv_-_BkKUUQI,16855
10
+ jarvis/jarvis_code_agent/code_agent.py,sha256=9R32DV0-II4bxV_8b0tsdZW4UsuZjCf_fbplG2u27G0,17091
11
11
  jarvis/jarvis_code_analysis/code_review.py,sha256=68GCjxdg-n2hoMoCSGWpH_ZdQArc4G09E9Wwhypk4eY,30135
12
12
  jarvis/jarvis_code_analysis/checklists/__init__.py,sha256=cKQ_FOGy5TQgM-YkRCqORo-mUOZaPAJ9VDmZoFX58us,78
13
13
  jarvis/jarvis_code_analysis/checklists/c_cpp.py,sha256=SXPpYCNeCtU1PpKdKPiYDuOybfY9vaL0ejDn4imxDwA,1317
@@ -30,7 +30,7 @@ jarvis/jarvis_code_analysis/checklists/sql.py,sha256=ecKKT6wJAibn8R0NxGZDNlm4teY
30
30
  jarvis/jarvis_code_analysis/checklists/swift.py,sha256=YcsYFxAitHqOtBZjG-RV9-KNM7X5lIcl6zlEI9XfmfM,2566
31
31
  jarvis/jarvis_code_analysis/checklists/web.py,sha256=-Pnj1FQTsGVZUQK7-4ptDsGd7a22Cs0585jRAPT2SdQ,3943
32
32
  jarvis/jarvis_data/huggingface.tar.gz,sha256=dWKnc_tvyx-I_ZkXo91O0b38KxDmLW1ZbmJ3E6fCl_k,1120205
33
- jarvis/jarvis_dev/main.py,sha256=zfL9rl-Jfhpi4E4OxMKw3eOVjy6kSzQdxhn3yGv1UTw,42952
33
+ jarvis/jarvis_dev/main.py,sha256=aqQ3SIek4jtGmkuzmt0FvZ27_9JtaE-dldqXQVH5fgg,42958
34
34
  jarvis/jarvis_git_details/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  jarvis/jarvis_git_details/main.py,sha256=l4Ol96DFISq2ctAgzmfUuS4i6JdWh0JAu_isY3iuzVo,8919
36
36
  jarvis/jarvis_git_squash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -45,18 +45,18 @@ jarvis/jarvis_lsp/rust.py,sha256=Z2KmvTsIcn5SLM4iaLG4Mael1h6zlZQIiOopbDpurag,371
45
45
  jarvis/jarvis_mcp/__init__.py,sha256=NF_vqRxaNyz8ColcpRh0bOkinV90YLAKHEN--jkP-B8,2114
46
46
  jarvis/jarvis_mcp/sse_mcp_client.py,sha256=FOVzroTw-pifmnF0qdsoQ6KweDCQ0Gxs6d6jl4VopiQ,23483
47
47
  jarvis/jarvis_mcp/stdio_mcp_client.py,sha256=KO0ewJuLBZLNqG4EGJcBOtn-8VJIipkq84ENvSwgQAo,11830
48
- jarvis/jarvis_methodology/main.py,sha256=_KDGEQw6j_VZ9O8eDe-c8F84zl6JrKmsNRva9PG4I5k,11836
49
- jarvis/jarvis_multi_agent/__init__.py,sha256=LEJofDjh80U34RyZv2ECAzpt2zkhA0Jn3KZh-ABoAKA,4343
48
+ jarvis/jarvis_methodology/main.py,sha256=QJUMIb9o8JO-l207X5UIbazZKJYKG3F4iuUKtkm0dmg,11840
49
+ jarvis/jarvis_multi_agent/__init__.py,sha256=Xab5sFltJmX_9MoXqanmZs6FqKfUb2v_pG29Vk8ZXaw,4311
50
50
  jarvis/jarvis_multi_agent/main.py,sha256=Z6N5VMjzaernnRjPkqgYRv09cIhWIFQ6a__AqHA8xrQ,1567
51
51
  jarvis/jarvis_platform/__init__.py,sha256=0YnsUoM4JkIBOtImFdjfuDbrqQZT3dEaAwSJ62DrpCc,104
52
52
  jarvis/jarvis_platform/base.py,sha256=WVxGxXU9DfwQdISlPxBA7NGRsbjVuOQXxIrxg0eaT04,5423
53
53
  jarvis/jarvis_platform/human.py,sha256=5hfYSp0K7VmB_J2IyLwaVGOrxsaNIEZwsWvs1nF224U,2518
54
- jarvis/jarvis_platform/kimi.py,sha256=yz7OVT54YDSUVuFlijDB7VdsJmtqRgj-qYKPHy8f0WQ,16787
55
- jarvis/jarvis_platform/openai.py,sha256=VDlWyOq4JrSPklz-D1Bu5DgDGRChebay5WYqSBXs-ww,4195
54
+ jarvis/jarvis_platform/kimi.py,sha256=rVpAHoxuCXPZo9tepbp1NIX2Q8RYgnXyOJj0VS_q868,20669
55
+ jarvis/jarvis_platform/openai.py,sha256=6qLCXoi15DGf9DvC9MUTha0gbZPoesFinixU2gSsBRI,5232
56
56
  jarvis/jarvis_platform/registry.py,sha256=UjCdPT9WIRxU-F0uuPpKmKRRCcNNxjr-bRTEPgRSNx4,7740
57
- jarvis/jarvis_platform/yuanbao.py,sha256=gPFy03DA0PemBNQTtxbDCBJ6mqUtSZclfiZbdNq1Ivk,22036
57
+ jarvis/jarvis_platform/yuanbao.py,sha256=yvAfvE9AyS-ZvOlHbwXwa6kHLt5IO778BzVbi0kvkw0,24013
58
58
  jarvis/jarvis_platform_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- jarvis/jarvis_platform_manager/main.py,sha256=gFqXKNweU9mE-IRqV7qhXmZ5483D01s9bI76POX1uXc,22596
59
+ jarvis/jarvis_platform_manager/main.py,sha256=VD0hKhLb8SevdFSYDXLRj3xwjBR1DTcRVgyn_Vp55VE,25593
60
60
  jarvis/jarvis_smart_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
61
  jarvis/jarvis_smart_shell/main.py,sha256=uq5NCdNAdcHqvtG0zpajz85SigSKH1SSAcEpyHa_BOc,5180
62
62
  jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -77,7 +77,7 @@ jarvis/jarvis_tools/lsp_get_diagnostics.py,sha256=paz1CVZ2Y8nk0U74n1QiG01oDINiZq
77
77
  jarvis/jarvis_tools/methodology.py,sha256=Md8W2et0xUiuTjUSRCdnlwEPYqah2dCAAkxW_95BXBY,5238
78
78
  jarvis/jarvis_tools/read_code.py,sha256=pgztSBRh8RORFalqwzzsLHQogooFvDm1ePBL0E5O1C4,5961
79
79
  jarvis/jarvis_tools/read_webpage.py,sha256=LLvAOvaQJodaeNJKQ6dU9MYEE227NMdHyLs7esluUQ4,2217
80
- jarvis/jarvis_tools/registry.py,sha256=tqw_umqj40bP2AxBYztTZGXZCe8F4qV0PzZqCDpOMhg,22602
80
+ jarvis/jarvis_tools/registry.py,sha256=eH-jTesZMAp4N8YNgs-eiRUwzksXB1mgMpWs-E8FrYY,22666
81
81
  jarvis/jarvis_tools/rewrite_file.py,sha256=rEPPSNU7uF1iKfEW9npEpZJ2LSoQXjt2OC-_troBToE,7003
82
82
  jarvis/jarvis_tools/search_web.py,sha256=-h1WYOqTcYC_8fdkm-4RfwKpbtLTVxOfRROul51NgO0,951
83
83
  jarvis/jarvis_tools/virtual_tty.py,sha256=AKAaKY5KcPxifNQoXjzHaL4U6EUVA7irHLwVvz2wLVs,16396
@@ -91,13 +91,13 @@ jarvis/jarvis_utils/file_processors.py,sha256=tSZSMJ4qCJ_lXI0dyLgJ0j5qEh6CDXDSVI
91
91
  jarvis/jarvis_utils/git_utils.py,sha256=MxhUcQ_gFUFyBxBiorEJ1wUk9a2TerFdq3-Z11FB-AE,11324
92
92
  jarvis/jarvis_utils/globals.py,sha256=Zs0chxA_giYiolYvawFFpcnTWgCUnn6GEusAh42jbz8,2275
93
93
  jarvis/jarvis_utils/input.py,sha256=qGf2q-yWhgT-OX-j_WYi7aZ11jYmuFNiMz2_W1nUOiM,7432
94
- jarvis/jarvis_utils/methodology.py,sha256=P1S_J9zxTed0Unpj0r-w14zpj8lEe_3bXgq1uvojjdQ,7512
95
- jarvis/jarvis_utils/output.py,sha256=PVG4fQ3P-eGOZUNZTowPtnjqq3GN91OE8fHa68lFOOg,8440
94
+ jarvis/jarvis_utils/methodology.py,sha256=9dmtj6Ei2CRUdQP9TA_xToqZPYcm5_DQovwnRkEShsA,8626
95
+ jarvis/jarvis_utils/output.py,sha256=7uhqaRCTFIKXhC8bvSQPNI3vyNoKEbzTTVFXbymzxZg,7726
96
96
  jarvis/jarvis_utils/tag.py,sha256=YJHmuedLb7_AiqvKQetHr4R1FxyzIh7HN0RRkWMmYbU,429
97
97
  jarvis/jarvis_utils/utils.py,sha256=iIcefLa1kvALkmQ19BLWDplmxrcOjtxWAgBIxrs8ytg,4439
98
- jarvis_ai_assistant-0.1.174.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
99
- jarvis_ai_assistant-0.1.174.dist-info/METADATA,sha256=NPd_DkLhSnLBXgjB6SjYhr5XhqCU41aPsDvF1WV_mgI,14543
100
- jarvis_ai_assistant-0.1.174.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
101
- jarvis_ai_assistant-0.1.174.dist-info/entry_points.txt,sha256=rjj61tZ7ahLi1R-JkJmX-IzIPPHD8mnwDZap1CnMe2s,973
102
- jarvis_ai_assistant-0.1.174.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
103
- jarvis_ai_assistant-0.1.174.dist-info/RECORD,,
98
+ jarvis_ai_assistant-0.1.175.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
99
+ jarvis_ai_assistant-0.1.175.dist-info/METADATA,sha256=MWbQAB8D5l9rPNNML1rtCawvNHha-s-sAwQGMVJUyUc,14543
100
+ jarvis_ai_assistant-0.1.175.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
101
+ jarvis_ai_assistant-0.1.175.dist-info/entry_points.txt,sha256=rjj61tZ7ahLi1R-JkJmX-IzIPPHD8mnwDZap1CnMe2s,973
102
+ jarvis_ai_assistant-0.1.175.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
103
+ jarvis_ai_assistant-0.1.175.dist-info/RECORD,,