jarvis-ai-assistant 0.7.8__py3-none-any.whl → 1.0.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.
Files changed (279) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +567 -222
  3. jarvis/jarvis_agent/agent_manager.py +19 -12
  4. jarvis/jarvis_agent/builtin_input_handler.py +79 -11
  5. jarvis/jarvis_agent/config_editor.py +7 -2
  6. jarvis/jarvis_agent/event_bus.py +24 -13
  7. jarvis/jarvis_agent/events.py +19 -1
  8. jarvis/jarvis_agent/file_context_handler.py +67 -64
  9. jarvis/jarvis_agent/file_methodology_manager.py +38 -24
  10. jarvis/jarvis_agent/jarvis.py +186 -114
  11. jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
  12. jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
  13. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
  14. jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
  15. jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
  16. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
  17. jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
  18. jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
  19. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
  20. jarvis/jarvis_agent/language_support_info.py +250 -219
  21. jarvis/jarvis_agent/main.py +19 -23
  22. jarvis/jarvis_agent/memory_manager.py +9 -6
  23. jarvis/jarvis_agent/methodology_share_manager.py +21 -15
  24. jarvis/jarvis_agent/output_handler.py +4 -2
  25. jarvis/jarvis_agent/prompt_builder.py +7 -6
  26. jarvis/jarvis_agent/prompt_manager.py +113 -8
  27. jarvis/jarvis_agent/prompts.py +317 -85
  28. jarvis/jarvis_agent/protocols.py +5 -2
  29. jarvis/jarvis_agent/run_loop.py +192 -32
  30. jarvis/jarvis_agent/session_manager.py +7 -3
  31. jarvis/jarvis_agent/share_manager.py +23 -13
  32. jarvis/jarvis_agent/shell_input_handler.py +12 -8
  33. jarvis/jarvis_agent/stdio_redirect.py +25 -26
  34. jarvis/jarvis_agent/task_analyzer.py +29 -23
  35. jarvis/jarvis_agent/task_list.py +869 -0
  36. jarvis/jarvis_agent/task_manager.py +26 -23
  37. jarvis/jarvis_agent/tool_executor.py +6 -5
  38. jarvis/jarvis_agent/tool_share_manager.py +24 -14
  39. jarvis/jarvis_agent/user_interaction.py +3 -3
  40. jarvis/jarvis_agent/utils.py +9 -1
  41. jarvis/jarvis_agent/web_bridge.py +37 -17
  42. jarvis/jarvis_agent/web_output_sink.py +5 -2
  43. jarvis/jarvis_agent/web_server.py +165 -36
  44. jarvis/jarvis_c2rust/__init__.py +1 -1
  45. jarvis/jarvis_c2rust/cli.py +260 -141
  46. jarvis/jarvis_c2rust/collector.py +37 -18
  47. jarvis/jarvis_c2rust/constants.py +60 -0
  48. jarvis/jarvis_c2rust/library_replacer.py +242 -1010
  49. jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
  50. jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
  51. jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
  52. jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
  53. jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
  54. jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
  55. jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
  56. jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
  57. jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
  58. jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
  59. jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
  60. jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
  61. jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
  62. jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
  63. jarvis/jarvis_c2rust/loaders.py +28 -10
  64. jarvis/jarvis_c2rust/models.py +5 -2
  65. jarvis/jarvis_c2rust/optimizer.py +192 -1974
  66. jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
  67. jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
  68. jarvis/jarvis_c2rust/optimizer_config.py +49 -0
  69. jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
  70. jarvis/jarvis_c2rust/optimizer_options.py +48 -0
  71. jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
  72. jarvis/jarvis_c2rust/optimizer_report.py +52 -0
  73. jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
  74. jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
  75. jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
  76. jarvis/jarvis_c2rust/scanner.py +229 -166
  77. jarvis/jarvis_c2rust/transpiler.py +531 -2732
  78. jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
  79. jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
  80. jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
  81. jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
  82. jarvis/jarvis_c2rust/transpiler_config.py +178 -0
  83. jarvis/jarvis_c2rust/transpiler_context.py +122 -0
  84. jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
  85. jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
  86. jarvis/jarvis_c2rust/transpiler_git.py +163 -0
  87. jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
  88. jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
  89. jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
  90. jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
  91. jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
  92. jarvis/jarvis_c2rust/utils.py +269 -79
  93. jarvis/jarvis_code_agent/after_change.py +233 -0
  94. jarvis/jarvis_code_agent/build_validation_config.py +37 -30
  95. jarvis/jarvis_code_agent/builtin_rules.py +68 -0
  96. jarvis/jarvis_code_agent/code_agent.py +976 -1517
  97. jarvis/jarvis_code_agent/code_agent_build.py +227 -0
  98. jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
  99. jarvis/jarvis_code_agent/code_agent_git.py +525 -0
  100. jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
  101. jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
  102. jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
  103. jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
  104. jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
  105. jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
  106. jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
  107. jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
  108. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
  109. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
  110. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
  111. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
  112. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
  113. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
  114. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
  115. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
  116. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
  117. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
  118. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
  119. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
  120. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
  121. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
  122. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
  123. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
  124. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
  125. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
  126. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
  127. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
  128. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
  129. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
  130. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
  131. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
  132. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
  133. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
  134. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
  135. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
  136. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
  137. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
  138. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
  139. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
  140. jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
  141. jarvis/jarvis_code_agent/lint.py +223 -524
  142. jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
  143. jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
  144. jarvis/jarvis_code_agent/rules/code_review.md +115 -0
  145. jarvis/jarvis_code_agent/rules/documentation.md +165 -0
  146. jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
  147. jarvis/jarvis_code_agent/rules/performance.md +158 -0
  148. jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
  149. jarvis/jarvis_code_agent/rules/security.md +160 -0
  150. jarvis/jarvis_code_agent/rules/tdd.md +78 -0
  151. jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
  152. jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
  153. jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
  154. jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
  155. jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
  156. jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
  157. jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
  158. jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
  159. jarvis/jarvis_code_agent/utils.py +36 -26
  160. jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
  161. jarvis/jarvis_code_analysis/code_review.py +64 -33
  162. jarvis/jarvis_data/config_schema.json +285 -192
  163. jarvis/jarvis_git_squash/main.py +8 -6
  164. jarvis/jarvis_git_utils/git_commiter.py +53 -76
  165. jarvis/jarvis_mcp/__init__.py +5 -2
  166. jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
  167. jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
  168. jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
  169. jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
  170. jarvis/jarvis_methodology/main.py +48 -39
  171. jarvis/jarvis_multi_agent/__init__.py +56 -23
  172. jarvis/jarvis_multi_agent/main.py +15 -18
  173. jarvis/jarvis_platform/base.py +179 -111
  174. jarvis/jarvis_platform/human.py +27 -16
  175. jarvis/jarvis_platform/kimi.py +52 -45
  176. jarvis/jarvis_platform/openai.py +101 -40
  177. jarvis/jarvis_platform/registry.py +51 -33
  178. jarvis/jarvis_platform/tongyi.py +68 -38
  179. jarvis/jarvis_platform/yuanbao.py +59 -43
  180. jarvis/jarvis_platform_manager/main.py +68 -76
  181. jarvis/jarvis_platform_manager/service.py +24 -14
  182. jarvis/jarvis_rag/README_CONFIG.md +314 -0
  183. jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
  184. jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
  185. jarvis/jarvis_rag/__init__.py +57 -4
  186. jarvis/jarvis_rag/cache.py +3 -1
  187. jarvis/jarvis_rag/cli.py +48 -68
  188. jarvis/jarvis_rag/embedding_interface.py +39 -0
  189. jarvis/jarvis_rag/embedding_manager.py +7 -230
  190. jarvis/jarvis_rag/embeddings/__init__.py +41 -0
  191. jarvis/jarvis_rag/embeddings/base.py +114 -0
  192. jarvis/jarvis_rag/embeddings/cohere.py +66 -0
  193. jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
  194. jarvis/jarvis_rag/embeddings/local.py +260 -0
  195. jarvis/jarvis_rag/embeddings/openai.py +62 -0
  196. jarvis/jarvis_rag/embeddings/registry.py +293 -0
  197. jarvis/jarvis_rag/llm_interface.py +8 -6
  198. jarvis/jarvis_rag/query_rewriter.py +8 -9
  199. jarvis/jarvis_rag/rag_pipeline.py +61 -52
  200. jarvis/jarvis_rag/reranker.py +7 -75
  201. jarvis/jarvis_rag/reranker_interface.py +32 -0
  202. jarvis/jarvis_rag/rerankers/__init__.py +41 -0
  203. jarvis/jarvis_rag/rerankers/base.py +109 -0
  204. jarvis/jarvis_rag/rerankers/cohere.py +67 -0
  205. jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
  206. jarvis/jarvis_rag/rerankers/jina.py +79 -0
  207. jarvis/jarvis_rag/rerankers/local.py +89 -0
  208. jarvis/jarvis_rag/rerankers/registry.py +293 -0
  209. jarvis/jarvis_rag/retriever.py +58 -43
  210. jarvis/jarvis_sec/__init__.py +66 -141
  211. jarvis/jarvis_sec/agents.py +21 -17
  212. jarvis/jarvis_sec/analysis.py +80 -33
  213. jarvis/jarvis_sec/checkers/__init__.py +7 -13
  214. jarvis/jarvis_sec/checkers/c_checker.py +356 -164
  215. jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
  216. jarvis/jarvis_sec/cli.py +43 -21
  217. jarvis/jarvis_sec/clustering.py +430 -272
  218. jarvis/jarvis_sec/file_manager.py +99 -55
  219. jarvis/jarvis_sec/parsers.py +9 -6
  220. jarvis/jarvis_sec/prompts.py +4 -3
  221. jarvis/jarvis_sec/report.py +44 -22
  222. jarvis/jarvis_sec/review.py +180 -107
  223. jarvis/jarvis_sec/status.py +50 -41
  224. jarvis/jarvis_sec/types.py +3 -0
  225. jarvis/jarvis_sec/utils.py +160 -83
  226. jarvis/jarvis_sec/verification.py +411 -181
  227. jarvis/jarvis_sec/workflow.py +132 -21
  228. jarvis/jarvis_smart_shell/main.py +28 -41
  229. jarvis/jarvis_stats/cli.py +14 -12
  230. jarvis/jarvis_stats/stats.py +28 -19
  231. jarvis/jarvis_stats/storage.py +14 -8
  232. jarvis/jarvis_stats/visualizer.py +12 -7
  233. jarvis/jarvis_tools/base.py +5 -2
  234. jarvis/jarvis_tools/clear_memory.py +13 -9
  235. jarvis/jarvis_tools/cli/main.py +23 -18
  236. jarvis/jarvis_tools/edit_file.py +572 -873
  237. jarvis/jarvis_tools/execute_script.py +10 -7
  238. jarvis/jarvis_tools/file_analyzer.py +7 -8
  239. jarvis/jarvis_tools/meta_agent.py +287 -0
  240. jarvis/jarvis_tools/methodology.py +5 -3
  241. jarvis/jarvis_tools/read_code.py +305 -1438
  242. jarvis/jarvis_tools/read_symbols.py +50 -17
  243. jarvis/jarvis_tools/read_webpage.py +19 -18
  244. jarvis/jarvis_tools/registry.py +435 -156
  245. jarvis/jarvis_tools/retrieve_memory.py +16 -11
  246. jarvis/jarvis_tools/save_memory.py +8 -6
  247. jarvis/jarvis_tools/search_web.py +31 -31
  248. jarvis/jarvis_tools/sub_agent.py +32 -28
  249. jarvis/jarvis_tools/sub_code_agent.py +44 -60
  250. jarvis/jarvis_tools/task_list_manager.py +1811 -0
  251. jarvis/jarvis_tools/virtual_tty.py +29 -19
  252. jarvis/jarvis_utils/__init__.py +4 -0
  253. jarvis/jarvis_utils/builtin_replace_map.py +2 -1
  254. jarvis/jarvis_utils/clipboard.py +9 -8
  255. jarvis/jarvis_utils/collections.py +331 -0
  256. jarvis/jarvis_utils/config.py +699 -194
  257. jarvis/jarvis_utils/dialogue_recorder.py +294 -0
  258. jarvis/jarvis_utils/embedding.py +6 -3
  259. jarvis/jarvis_utils/file_processors.py +7 -1
  260. jarvis/jarvis_utils/fzf.py +9 -3
  261. jarvis/jarvis_utils/git_utils.py +71 -42
  262. jarvis/jarvis_utils/globals.py +116 -32
  263. jarvis/jarvis_utils/http.py +6 -2
  264. jarvis/jarvis_utils/input.py +318 -83
  265. jarvis/jarvis_utils/jsonnet_compat.py +119 -104
  266. jarvis/jarvis_utils/methodology.py +37 -28
  267. jarvis/jarvis_utils/output.py +201 -44
  268. jarvis/jarvis_utils/utils.py +986 -628
  269. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
  270. jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
  271. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
  272. jarvis/jarvis_tools/generate_new_tool.py +0 -205
  273. jarvis/jarvis_tools/lsp_client.py +0 -1552
  274. jarvis/jarvis_tools/rewrite_file.py +0 -105
  275. jarvis_ai_assistant-0.7.8.dist-info/RECORD +0 -218
  276. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
  277. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
  278. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
  279. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
@@ -10,49 +10,46 @@ import _jsonnet
10
10
  def _fix_jsonnet_multiline_strings(s: str) -> tuple[str, dict]:
11
11
  """
12
12
  修复 jsonnet ||| 多行字符串的缩进问题。
13
-
13
+
14
14
  jsonnet 要求 ||| 之后的第一行内容必须有缩进(至少一个空格),
15
15
  否则会报错 "text block's first line must start with whitespace"。
16
-
16
+
17
17
  此函数会自动检测并修复这个问题。
18
-
18
+
19
19
  参数:
20
20
  s: 输入字符串
21
-
21
+
22
22
  返回:
23
23
  (修复后的字符串, 第一行原始缩进信息字典)
24
24
  缩进信息字典的键是修复后字符串中 ||| 多行字符串的标记,
25
25
  值是第一行的原始缩进级别(如果第一行原本有缩进但后续行没有)
26
26
  """
27
- if not isinstance(s, str):
28
- return s
29
-
27
+
30
28
  import re
31
-
29
+
32
30
  # 匹配 ||| 多行字符串模式
33
31
  # 格式:||| 后跟可选空白和换行,然后是内容,最后是 |||
34
32
  # 使用非贪婪匹配,确保匹配到最近的 |||
35
- pattern = r'(\|\|\|)(\s*\n)(.*?)(\n\s*\|\|\|)'
36
-
33
+ pattern = r"(\|\|\|)(\s*\n)(.*?)(\n\s*\|\|\|)"
34
+
37
35
  def fix_match(match):
38
36
  start_marker = match.group(1) # |||
39
37
  whitespace_after = match.group(2) # 空白和换行
40
38
  content = match.group(3) # 多行内容
41
39
  end_marker = match.group(4) # 换行、空白和 |||
42
-
40
+
43
41
  # jsonnet 要求结束标记 ||| 必须单独一行且没有缩进(从行首开始)
44
42
  # 无论原来是什么格式,统一修复为 '\n|||'
45
- end_marker = '\n|||'
46
-
43
+ end_marker = "\n|||"
47
44
 
48
45
  # 如果内容为空,返回修复后的结束标记
49
46
  if not content.strip():
50
47
  return start_marker + whitespace_after + content + end_marker, {}
51
-
48
+
52
49
  # 按行分割内容
53
50
 
54
- lines = content.split('\n')
55
-
51
+ lines = content.split("\n")
52
+
56
53
  # 确定缩进级别:
57
54
  # 1. 如果第一行有缩进,使用第一行的缩进级别
58
55
  # 2. 如果第一行是空行,查找第一个非空行的缩进级别
@@ -61,10 +58,10 @@ def _fix_jsonnet_multiline_strings(s: str) -> tuple[str, dict]:
61
58
  indent_level = 1 # 默认缩进级别
62
59
  first_line_has_indent = False
63
60
  first_line_indent = 0
64
-
61
+
65
62
  if lines:
66
63
  first_line = lines[0]
67
- if first_line.strip() and first_line.startswith((' ', '\t')):
64
+ if first_line.strip() and first_line.startswith((" ", "\t")):
68
65
  # 第一行已有缩进,记录其缩进级别
69
66
  first_line_indent = len(first_line) - len(first_line.lstrip())
70
67
  first_line_has_indent = True
@@ -76,7 +73,7 @@ def _fix_jsonnet_multiline_strings(s: str) -> tuple[str, dict]:
76
73
  # 第一行是空行,查找第一个非空行的缩进级别
77
74
  for line in lines[1:]:
78
75
  if line.strip():
79
- if line.startswith((' ', '\t')):
76
+ if line.startswith((" ", "\t")):
80
77
  # 找到第一个非空行,使用其缩进级别
81
78
  indent_level = len(line) - len(line.lstrip())
82
79
  # 确保至少有一个空格
@@ -86,43 +83,41 @@ def _fix_jsonnet_multiline_strings(s: str) -> tuple[str, dict]:
86
83
  # 第一个非空行没有缩进,使用默认的一个空格
87
84
  indent_level = 1
88
85
  break
89
-
86
+
90
87
  # 对每一行都统一缩进级别
91
88
  # jsonnet 的 ||| 要求所有行都有相同的缩进级别,并会去除所有行的最小共同缩进前缀
92
89
  # 为了保留第一行的缩进,我们需要:
93
90
  # 1. 让所有行都有相同的缩进(满足 jsonnet 的要求)
94
91
  # 2. 记录第一行的原始缩进级别,以便在解析后恢复
95
92
  # 3. 在解析后,为第一行添加原始缩进
96
-
93
+
97
94
  # 检查是否有后续行没有缩进(需要修复)
98
95
  has_unindented_lines = False
99
96
  if first_line_has_indent:
100
97
  for line in lines[1:]:
101
- if line.strip() and not line.startswith((' ', '\t')):
98
+ if line.strip() and not line.startswith((" ", "\t")):
102
99
  has_unindented_lines = True
103
100
  break
104
-
101
+
105
102
  # 记录所有行的原始缩进信息(用于恢复)
106
103
  # 如果存在不同缩进级别的行,我们需要记录每行的原始缩进
107
104
  original_indents = {} # 键:行内容(去除缩进后),值:原始缩进级别
108
105
  has_mixed_indents = False
109
-
106
+
110
107
  # 记录所有行的原始缩进信息(无论是否混合缩进,都需要记录以便恢复)
111
108
  if lines:
112
109
  seen_indents = set()
113
110
  for line in lines:
114
-
115
111
  if line.strip():
116
112
  line_indent = len(line) - len(line.lstrip())
117
113
  seen_indents.add(line_indent)
118
114
  line_content = line.lstrip()
119
115
  # 记录原始缩进(包括0缩进的情况,用特殊值标记)
120
116
  original_indents[line_content] = line_indent
121
-
117
+
122
118
  # 如果有多个不同的缩进级别,说明是混合缩进
123
119
  if len(seen_indents) > 1:
124
120
  has_mixed_indents = True
125
-
126
121
 
127
122
  # 如果第一行有缩进,但后续行没有,我们也需要记录
128
123
  if first_line_has_indent and has_unindented_lines:
@@ -130,7 +125,7 @@ def _fix_jsonnet_multiline_strings(s: str) -> tuple[str, dict]:
130
125
  # 记录第一行的原始缩进
131
126
  first_line_content = lines[0].lstrip()
132
127
  original_indents[first_line_content] = first_line_indent
133
-
128
+
134
129
  # jsonnet的text block规则:所有行缩进必须 >= 首行缩进
135
130
  # 因此我们统一所有行为相同的基础缩进,通过恢复逻辑还原原始缩进
136
131
  base_indent = 1 # 统一使用1空格缩进
@@ -141,59 +136,57 @@ def _fix_jsonnet_multiline_strings(s: str) -> tuple[str, dict]:
141
136
  line = lines[i]
142
137
  if line.strip(): # 只处理非空行
143
138
  line_content = line.lstrip()
144
- lines[i] = ' ' * base_indent + line_content
139
+ lines[i] = " " * base_indent + line_content
145
140
 
146
-
147
141
  # 重新组合内容
148
- fixed_content = '\n'.join(lines)
149
-
142
+ fixed_content = "\n".join(lines)
143
+
150
144
  # 返回修复后的内容和原始缩进信息
151
145
  # 只要有混合缩进,就返回缩进信息以便恢复
152
146
  indent_info = original_indents if has_mixed_indents else {}
153
-
147
+
154
148
  return start_marker + whitespace_after + fixed_content + end_marker, indent_info
155
-
156
149
 
157
150
  # 使用 DOTALL 标志,使 . 匹配换行符
158
151
  # 收集所有修复后的内容和缩进信息
159
152
  all_indent_info = {}
160
153
  fixed_parts = []
161
154
  last_end = 0
162
-
155
+
163
156
  for match in re.finditer(pattern, s, flags=re.DOTALL):
164
157
  # 添加匹配前的部分
165
- fixed_parts.append(s[last_end:match.start()])
166
-
158
+ fixed_parts.append(s[last_end : match.start()])
159
+
167
160
  # 修复匹配的部分
168
161
  fixed_content, indent_info = fix_match(match)
169
162
  fixed_parts.append(fixed_content)
170
-
163
+
171
164
  # 合并缩进信息
172
165
  all_indent_info.update(indent_info)
173
-
166
+
174
167
  last_end = match.end()
175
-
168
+
176
169
  # 添加剩余部分
177
170
  fixed_parts.append(s[last_end:])
178
- fixed = ''.join(fixed_parts)
179
-
171
+ fixed = "".join(fixed_parts)
172
+
180
173
  return fixed, all_indent_info
181
174
 
182
175
 
183
176
  def _restore_first_line_indent(obj: Any, indent_info: dict) -> Any:
184
177
  """
185
178
  恢复第一行的原始缩进。
186
-
179
+
187
180
  参数:
188
181
  obj: 解析后的对象
189
182
  indent_info: 缩进信息字典
190
-
183
+
191
184
  返回:
192
185
  恢复缩进后的对象
193
186
  """
194
187
  if not indent_info:
195
188
  return obj
196
-
189
+
197
190
  if isinstance(obj, dict):
198
191
  return {k: _restore_first_line_indent(v, indent_info) for k, v in obj.items()}
199
192
  elif isinstance(obj, list):
@@ -202,7 +195,7 @@ def _restore_first_line_indent(obj: Any, indent_info: dict) -> Any:
202
195
  # 检查字符串的每一行是否原本有缩进
203
196
  # 如果存在原始缩进信息,恢复每行的原始缩进
204
197
  if indent_info:
205
- lines = obj.split('\n')
198
+ lines = obj.split("\n")
206
199
  restored_lines = []
207
200
  for line in lines:
208
201
  if line.strip():
@@ -212,14 +205,14 @@ def _restore_first_line_indent(obj: Any, indent_info: dict) -> Any:
212
205
  if line_content in indent_info:
213
206
  # 恢复原始缩进
214
207
  original_indent = indent_info[line_content]
215
- restored_lines.append(' ' * original_indent + line_content)
208
+ restored_lines.append(" " * original_indent + line_content)
216
209
  else:
217
210
  # 没有原始缩进信息,保持原样
218
211
  restored_lines.append(line)
219
212
  else:
220
213
  # 空行保持原样
221
214
  restored_lines.append(line)
222
- return '\n'.join(restored_lines)
215
+ return "\n".join(restored_lines)
223
216
  return obj
224
217
  else:
225
218
  return obj
@@ -228,96 +221,96 @@ def _restore_first_line_indent(obj: Any, indent_info: dict) -> Any:
228
221
  def _convert_backtick_multiline_strings(s: str) -> str:
229
222
  """
230
223
  将 JSON 值中的 ``` 多行字符串标识转换为 |||。
231
-
224
+
232
225
  此函数识别 JSON 值位置(如 "key": ```)的 ``` 标记,并将其转换为 |||,
233
226
  以便与 jsonnet 的 ||| 多行字符串语法兼容。
234
-
227
+
235
228
  识别规则:
236
229
  - 在 JSON 值位置(冒号后)的 ``` 会被转换为 |||
237
230
  - 已经去除 markdown 代码块标记后,剩余的 ``` 通常是多行字符串标识
238
-
231
+
239
232
  参数:
240
233
  s: 输入字符串(应该已经去除 markdown 代码块标记)
241
-
234
+
242
235
  返回:
243
236
  转换后的字符串(``` 转换为 |||)
244
237
  """
245
- if not isinstance(s, str):
246
- return s
247
-
238
+
248
239
  import re
249
-
240
+
250
241
  # 匹配 JSON 值中的 ``` 多行字符串
251
242
  # 格式:": ``` 或 ":``` 后跟可选空白和换行,然后是内容,最后是换行和 ```
252
243
  # 使用非贪婪匹配,确保匹配到最近的 ```
253
244
  # 注意:这个模式匹配的是 JSON 值位置(冒号后)的 ```
254
- pattern = r'(:\s*)(```)(\s*\n)(.*?)(\n\s*```)'
255
-
245
+ pattern = r"(:\s*)(```)(\s*\n)(.*?)(\n\s*```)"
246
+
256
247
  def convert_match(match):
257
248
  colon = match.group(1) # 冒号和可选空白
258
249
  match.group(2) # ``` (保留用于匹配,但不使用)
259
250
  whitespace_after = match.group(3) # 空白和换行
260
251
  content = match.group(4) # 多行内容
261
252
  match.group(5) # 换行、空白和 ``` (保留用于匹配,但不使用)
262
-
253
+
263
254
  # 将 ``` 转换为 |||
264
- return colon + '|||' + whitespace_after + content + '\n|||'
265
-
255
+ return colon + "|||" + whitespace_after + content + "\n|||"
256
+
266
257
  # 替换所有匹配的模式
267
258
  result = re.sub(pattern, convert_match, s, flags=re.DOTALL)
268
-
259
+
269
260
  return result
270
261
 
271
262
 
272
263
  def _strip_markdown_code_blocks(s: str) -> str:
273
264
  """
274
265
  去除字符串中的 markdown 代码块标记(如 ```json5、```json、``` 等)
275
-
266
+
276
267
  支持以下场景:
277
268
  - 代码块前后有空白/换行:\n```json\n{...}\n```
278
269
  - 代码块不在字符串开头:prefix\n```json\n{...}\n```
279
270
  - 标准格式:```json\n{...}\n```
280
-
271
+
281
272
  参数:
282
273
  s: 输入字符串
283
-
274
+
284
275
  返回:
285
- 清理后的字符串
276
+ 清理后的字符串(如果输入不是字符串,则原样返回)
286
277
  """
278
+
279
+ import re
280
+
281
+ # 如果输入不是字符串,直接返回
287
282
  if not isinstance(s, str):
288
283
  return s
289
-
290
- import re
291
-
284
+
292
285
  # 先去除首尾空白,但保留内部结构
293
286
  block = s.strip()
294
-
287
+
295
288
  # 使用正则表达式匹配并去除代码块标记
296
289
  # 尝试多种模式,从严格到宽松
297
-
290
+
298
291
  # 模式1:标准格式,代码块在开头和结尾
299
292
  # 匹配:```language + 可选空白 + 可选换行 + 内容 + 可选换行 + 可选空白 + ```
300
- pattern1 = r'^```[a-zA-Z0-9_+-]*\s*\n?(.*?)\n?\s*```\s*$'
293
+ pattern1 = r"^```[a-zA-Z0-9_+-]*\s*\n?(.*?)\n?\s*```\s*$"
301
294
  match = re.match(pattern1, block, re.DOTALL)
302
295
  if match:
303
296
  return match.group(1).strip()
304
-
297
+
305
298
  # 模式2:代码块前后可能有额外空白/换行,但要求代码块在字符串的开头或结尾
306
299
  # 只匹配整个字符串被代码块包裹的情况,不匹配 JSON 值内部的 ```
307
300
  # 匹配:字符串开头(可选空白)+ ```language + 可选空白 + 换行 + 内容 + 换行 + 可选空白 + ``` + 字符串结尾(可选空白)
308
- pattern2 = r'^\s*```[a-zA-Z0-9_+-]*\s*\n(.*?)\n\s*```\s*$'
301
+ pattern2 = r"^\s*```[a-zA-Z0-9_+-]*\s*\n(.*?)\n\s*```\s*$"
309
302
  match = re.match(pattern2, block, re.DOTALL)
310
303
  if match:
311
304
  return match.group(1).strip()
312
-
305
+
313
306
  # 模式3:更宽松的匹配,不要求换行,但要求代码块在字符串的开头或结尾
314
307
  # 只匹配整个字符串被代码块包裹的情况,不匹配 JSON 值内部的 ```
315
308
  # 匹配:字符串开头(可选空白)+ ```language + 可选空白 + 内容 + 可选空白 + ``` + 字符串结尾(可选空白)
316
- pattern3 = r'^\s*```[a-zA-Z0-9_+-]*\s*(.*?)\s*```\s*$'
309
+ pattern3 = r"^\s*```[a-zA-Z0-9_+-]*\s*(.*?)\s*```\s*$"
317
310
  match = re.match(pattern3, block, re.DOTALL)
318
311
  if match:
319
312
  return match.group(1).strip()
320
-
313
+
321
314
  # 如果正则都不匹配,尝试手动去除(向后兼容)
322
315
  # 但只处理整个字符串被代码块包裹的情况(代码块在开头且结尾也有 ```)
323
316
  block_stripped = block.strip()
@@ -325,56 +318,71 @@ def _strip_markdown_code_blocks(s: str) -> str:
325
318
  # 找到开头的 ``` 后的内容
326
319
  after_start = 3 # 跳过 ```
327
320
  # 跳过语言标识(如果有)
328
- while after_start < len(block_stripped) and block_stripped[after_start] not in ('\n', '\r', ' ', '\t'):
321
+ while after_start < len(block_stripped) and block_stripped[after_start] not in (
322
+ "\n",
323
+ "\r",
324
+ " ",
325
+ "\t",
326
+ ):
329
327
  after_start += 1
330
328
  # 跳过空白字符
331
- while after_start < len(block_stripped) and block_stripped[after_start] in (' ', '\t'):
329
+ while after_start < len(block_stripped) and block_stripped[after_start] in (
330
+ " ",
331
+ "\t",
332
+ ):
332
333
  after_start += 1
333
334
  # 跳过换行符(如果有)
334
- if after_start < len(block_stripped) and block_stripped[after_start] in ('\n', '\r'):
335
+ if after_start < len(block_stripped) and block_stripped[after_start] in (
336
+ "\n",
337
+ "\r",
338
+ ):
335
339
  after_start += 1
336
340
  # 处理 \r\n
337
- if after_start < len(block_stripped) and block_stripped[after_start] == '\n' and block_stripped[after_start - 1] == '\r':
341
+ if (
342
+ after_start < len(block_stripped)
343
+ and block_stripped[after_start] == "\n"
344
+ and block_stripped[after_start - 1] == "\r"
345
+ ):
338
346
  after_start += 1
339
-
347
+
340
348
  # 找到结尾的 ``` 的位置
341
349
  before_end = block_stripped.rfind("```")
342
350
  if before_end > after_start:
343
351
  # 提取内容(去除结尾的 ``` 和前面的空白)
344
352
  content = block_stripped[after_start:before_end].rstrip()
345
353
  return content
346
-
354
+
347
355
  return block.strip()
348
356
 
349
357
 
350
358
  def loads(s: str) -> Any:
351
359
  """
352
360
  解析 JSON/Jsonnet 格式的字符串,返回 Python 对象
353
-
361
+
354
362
  使用 jsonnet 来解析,支持 JSON5 特性(注释、尾随逗号、||| 或 ``` 分隔符多行字符串等)
355
-
363
+
356
364
  自动处理:
357
365
  - markdown 代码块标记:如果输入包含 ```json5、```json、``` 等代码块标记,
358
366
  会自动去除这些标记后再解析。
359
367
  - ``` 多行字符串:支持使用 ``` 代替 ||| 作为多行字符串标识(在 JSON 值位置)。
360
368
  - ||| 多行字符串缩进:自动为 ||| 多行字符串的第一行添加必要的缩进,
361
369
  避免 "text block's first line must start with whitespace" 错误。
362
-
370
+
363
371
  参数:
364
372
  s: 要解析的字符串(可能包含 markdown 代码块标记)
365
-
373
+
366
374
  返回:
367
375
  解析后的 Python 对象
368
-
376
+
369
377
  异常:
370
378
  ValueError: 如果解析失败
371
379
  """
372
380
  if not isinstance(s, str) or not s.strip():
373
381
  raise ValueError("输入字符串为空")
374
-
382
+
375
383
  # 自动去除 markdown 代码块标记
376
384
  cleaned = _strip_markdown_code_blocks(s)
377
-
385
+
378
386
  # 验证:确保没有残留的代码块标记(在字符串开头或结尾)
379
387
  # 字符串内容中的 ``` 是合法的,不需要处理
380
388
  cleaned_stripped = cleaned.strip()
@@ -384,18 +392,25 @@ def loads(s: str) -> Any:
384
392
  cleaned = _strip_markdown_code_blocks(cleaned)
385
393
  cleaned_stripped = cleaned.strip()
386
394
  # 如果仍然有,说明可能是格式问题,记录警告但继续处理
387
- if cleaned_stripped.startswith("```") or cleaned_stripped.rstrip().endswith("```"):
395
+ if cleaned_stripped.startswith("```") or cleaned_stripped.rstrip().endswith(
396
+ "```"
397
+ ):
388
398
  # 最后尝试:手动去除开头和结尾的 ```
389
399
  while cleaned_stripped.startswith("```"):
390
400
  # 找到第一个换行或字符串结尾
391
401
  first_newline = cleaned_stripped.find("\n", 3)
392
402
  if first_newline >= 0:
393
- cleaned_stripped = cleaned_stripped[first_newline + 1:]
403
+ cleaned_stripped = cleaned_stripped[first_newline + 1 :]
394
404
  else:
395
405
  # 没有换行,可能是 ```language 格式
396
406
  cleaned_stripped = cleaned_stripped[3:].lstrip()
397
407
  # 跳过语言标识
398
- while cleaned_stripped and cleaned_stripped[0] not in ('\n', '\r', ' ', '\t'):
408
+ while cleaned_stripped and cleaned_stripped[0] not in (
409
+ "\n",
410
+ "\r",
411
+ " ",
412
+ "\t",
413
+ ):
399
414
  cleaned_stripped = cleaned_stripped[1:]
400
415
  break
401
416
  while cleaned_stripped.rstrip().endswith("```"):
@@ -405,13 +420,13 @@ def loads(s: str) -> Any:
405
420
  else:
406
421
  break
407
422
  cleaned = cleaned_stripped
408
-
423
+
409
424
  # 将 JSON 值中的 ``` 多行字符串标识转换为 |||
410
425
  cleaned = _convert_backtick_multiline_strings(cleaned)
411
-
426
+
412
427
  # 自动修复 ||| 多行字符串的缩进问题
413
428
  cleaned, indent_info = _fix_jsonnet_multiline_strings(cleaned)
414
-
429
+
415
430
  # 使用 jsonnet 解析,支持 JSON5 和 Jsonnet 语法
416
431
  try:
417
432
  result_json = _jsonnet.evaluate_snippet("<input>", cleaned)
@@ -423,10 +438,11 @@ def loads(s: str) -> Any:
423
438
  if "```" in cleaned:
424
439
  # 找到所有 ``` 的位置
425
440
  import re
426
- matches = list(re.finditer(r'```', cleaned))
441
+
442
+ matches = list(re.finditer(r"```", cleaned))
427
443
  for match in matches:
428
444
  pos = match.start()
429
- context = cleaned[max(0, pos-30):min(len(cleaned), pos+50)]
445
+ context = cleaned[max(0, pos - 30) : min(len(cleaned), pos + 50)]
430
446
  # 检查是否在字符串内部(被引号包围)
431
447
  before = cleaned[:pos]
432
448
  # 简单检查:如果前面有奇数个引号,说明在字符串内部
@@ -439,27 +455,26 @@ def loads(s: str) -> Any:
439
455
  f"原始错误: {error_msg}"
440
456
  )
441
457
  raise ValueError(f"JSON 解析失败: {error_msg}")
442
-
458
+
443
459
  # jsonnet 返回的是 JSON 字符串,需要再次解析
444
460
  result = json.loads(result_json)
445
-
461
+
446
462
  # 如果第一行原本有缩进,恢复第一行的缩进
447
463
  if indent_info:
448
464
  result = _restore_first_line_indent(result, indent_info)
449
-
465
+
450
466
  return result
451
467
 
452
468
 
453
469
  def dumps(obj: Any, **kwargs) -> str:
454
470
  """
455
471
  将 Python 对象序列化为 JSON 字符串
456
-
472
+
457
473
  参数:
458
474
  obj: 要序列化的对象
459
475
  **kwargs: 传递给 json.dumps 的其他参数
460
-
476
+
461
477
  返回:
462
478
  JSON 字符串
463
479
  """
464
480
  return json.dumps(obj, **kwargs)
465
-