beswarm 0.3.12__tar.gz → 0.3.14__tar.gz
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.
- {beswarm-0.3.12 → beswarm-0.3.14}/PKG-INFO +1 -1
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/architext/architext/core.py +92 -41
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/architext/test/test.py +127 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/request.py +10 -7
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/utils.py +1 -1
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/models/chatgpt.py +18 -1
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/read_file.py +15 -23
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm.egg-info/PKG-INFO +1 -1
- {beswarm-0.3.12 → beswarm-0.3.14}/pyproject.toml +1 -1
- {beswarm-0.3.12 → beswarm-0.3.14}/MANIFEST.in +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/README.md +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/agents/chatgroup.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/agents/planact.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/architext/architext/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/architext/test/openai_client.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/architext/test/test_save_load.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/log_config.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/models.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/response.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/test/test_base_api.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/test/test_geminimask.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/test/test_image.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/core/test/test_payload.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/models/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/models/audio.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/models/base.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/arXiv.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/config.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/excute_command.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/get_time.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/image.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/list_directory.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/read_image.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/readonly.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/registry.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/run_python.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/plugins/websearch.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/utils/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/utils/prompt.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/aient/utils/scripts.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/test/test_Web_crawler.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/test/test_ddg_search.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/test/test_google_search.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/test/test_ollama.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/test/test_plugin.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/test/test_url.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/aient/test/test_whisper.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/bemcp/bemcp/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/bemcp/bemcp/decorator.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/bemcp/bemcp/main.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/bemcp/bemcp/utils.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/bemcp/test/client.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/bemcp/test/server.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/broker.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/cli.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/core.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/knowledge_graph.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/prompt.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/README.md +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/arduino-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/c-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/chatito-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/commonlisp-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/cpp-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/csharp-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/d-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/dart-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/elisp-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/elixir-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/elm-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/gleam-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/go-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/java-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/javascript-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/lua-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/pony-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/properties-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/python-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/r-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/racket-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/ruby-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/rust-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/solidity-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/swift-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/udev-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/README.md +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/c-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/cpp-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/dart-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/elisp-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/elixir-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/elm-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/go-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/hcl-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/java-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/javascript-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/php-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/python-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/ql-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/ruby-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/rust-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/scala-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-languages/typescript-tags.scm +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/taskmanager.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/__init__.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/click.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/completion.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/deep_search.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/edit_file.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/graph.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/planner.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/repomap.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/request_input.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/screenshot.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/search_arxiv.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/search_web.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/subtasks.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/worker.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/write_csv.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/tools/write_file.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm/utils.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm.egg-info/SOURCES.txt +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm.egg-info/dependency_links.txt +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm.egg-info/entry_points.txt +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm.egg-info/requires.txt +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/beswarm.egg-info/top_level.txt +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/setup.cfg +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/test/test_TaskManager.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/test/test_broker.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/test/test_graph.py +0 -0
- {beswarm-0.3.12 → beswarm-0.3.14}/test/test_new_TaskManager.py +0 -0
@@ -233,73 +233,116 @@ class Files(ContextProvider):
|
|
233
233
|
def __init__(self, *paths: Union[str, List[str]], name: str = "files", visible: bool = True):
|
234
234
|
super().__init__(name, visible=visible)
|
235
235
|
self._files: Dict[str, str] = {}
|
236
|
+
self._file_sources: Dict[str, Dict] = {}
|
236
237
|
|
237
238
|
file_paths: List[str] = []
|
238
239
|
if paths:
|
239
|
-
# Handle the case where the first argument is a list of paths, e.g., Files(['a', 'b'])
|
240
240
|
if len(paths) == 1 and isinstance(paths[0], list):
|
241
241
|
file_paths.extend(paths[0])
|
242
|
-
# Handle the case where arguments are individual string paths, e.g., Files('a', 'b')
|
243
242
|
else:
|
244
243
|
file_paths.extend(paths)
|
245
244
|
|
246
245
|
if file_paths:
|
247
246
|
for path in file_paths:
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
247
|
+
self.update(path)
|
248
|
+
|
249
|
+
def _read_from_disk(self, path: str, head: Optional[int] = None) -> str:
|
250
|
+
"""Reads content from a file on disk, respecting the head parameter."""
|
251
|
+
try:
|
252
|
+
with open(path, 'r', encoding='utf-8') as f:
|
253
|
+
if head is not None and head > 0:
|
254
|
+
lines = []
|
255
|
+
for _ in range(head):
|
256
|
+
try:
|
257
|
+
lines.append(next(f))
|
258
|
+
except StopIteration:
|
259
|
+
break
|
260
|
+
return "".join(lines).rstrip('\n')
|
261
|
+
else:
|
262
|
+
return f.read()
|
263
|
+
except FileNotFoundError:
|
264
|
+
raise
|
265
|
+
except Exception as e:
|
266
|
+
logging.error(f"Error reading file {path}: {e}")
|
267
|
+
return f"[Error: Could not read file at path '{path}': {e}]"
|
255
268
|
|
256
269
|
async def refresh(self):
|
257
270
|
"""
|
258
|
-
|
259
|
-
|
260
|
-
content is updated to reflect the error.
|
271
|
+
Synchronizes content for files sourced from disk.
|
272
|
+
Content set manually is overwritten if the file exists, but preserved if it does not.
|
261
273
|
"""
|
262
274
|
is_changed = False
|
263
|
-
for path in list(self.
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
self._files
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
self._files
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
275
|
+
for path, spec in list(self._file_sources.items()):
|
276
|
+
if spec.get('source') == 'disk':
|
277
|
+
try:
|
278
|
+
head = spec.get('head')
|
279
|
+
new_content = self._read_from_disk(path, head)
|
280
|
+
if self._files.get(path) != new_content:
|
281
|
+
self._files[path] = new_content
|
282
|
+
is_changed = True
|
283
|
+
except FileNotFoundError:
|
284
|
+
error_msg = f"[Error: File not found at path '{path}']"
|
285
|
+
if self._files.get(path) != error_msg:
|
286
|
+
self._files[path] = error_msg
|
287
|
+
is_changed = True
|
288
|
+
elif spec.get('source') == 'manual':
|
289
|
+
try:
|
290
|
+
# File exists, so we must overwrite manual content.
|
291
|
+
head = spec.get('head')
|
292
|
+
new_content = self._read_from_disk(path, head)
|
293
|
+
if self._files.get(path) != new_content:
|
294
|
+
self._files[path] = new_content
|
295
|
+
is_changed = True
|
296
|
+
# If we are here, manual content was overwritten by disk content,
|
297
|
+
# so we should update the source.
|
298
|
+
self._file_sources[path] = {'source': 'disk'}
|
299
|
+
|
300
|
+
except FileNotFoundError:
|
301
|
+
# File does not exist, so we keep the manual content. No change.
|
302
|
+
pass
|
280
303
|
|
281
304
|
if is_changed:
|
282
305
|
self.mark_stale()
|
283
|
-
|
284
306
|
await super().refresh()
|
285
307
|
|
286
|
-
def update(self, path: str, content: Optional[str] = None):
|
308
|
+
def update(self, path: str, content: Optional[str] = None, head: Optional[Union[int, str]] = None):
|
287
309
|
"""
|
288
|
-
Updates a single file
|
289
|
-
memory. If content is None, it reads the file from disk.
|
310
|
+
Updates a single file's content and its source specification.
|
290
311
|
"""
|
312
|
+
if head is not None:
|
313
|
+
try:
|
314
|
+
head = int(head)
|
315
|
+
except (ValueError, TypeError):
|
316
|
+
logging.warning(f"Invalid 'head' parameter for file '{path}': {head}. Must be an integer. Ignoring.")
|
317
|
+
head = None
|
318
|
+
|
291
319
|
if content is not None:
|
292
|
-
|
320
|
+
# New logic: if head is also provided, decide what to do with content.
|
321
|
+
if head is not None and head > 0:
|
322
|
+
try:
|
323
|
+
# If file exists, prioritize reading from disk.
|
324
|
+
self._files[path] = self._read_from_disk(path, head)
|
325
|
+
self._file_sources[path] = {'source': 'disk', 'head': head}
|
326
|
+
except FileNotFoundError:
|
327
|
+
# If file does not exist, use the provided content's head.
|
328
|
+
lines = content.split('\n')
|
329
|
+
self._files[path] = "\n".join(lines[:head])
|
330
|
+
self._file_sources[path] = {'source': 'manual', 'head': head}
|
331
|
+
else:
|
332
|
+
# Original logic for when only content is provided.
|
333
|
+
self._files[path] = content
|
334
|
+
self._file_sources[path] = {'source': 'manual'}
|
293
335
|
else:
|
336
|
+
# Original logic for when only path (and optional head) is provided.
|
294
337
|
try:
|
295
|
-
|
296
|
-
|
338
|
+
self._files[path] = self._read_from_disk(path, head)
|
339
|
+
spec = {'source': 'disk'}
|
340
|
+
if head is not None and head > 0:
|
341
|
+
spec['head'] = head
|
342
|
+
self._file_sources[path] = spec
|
297
343
|
except FileNotFoundError:
|
298
|
-
logging.error(f"File not found for update: {path}.")
|
299
344
|
self._files[path] = f"[Error: File not found at path '{path}']"
|
300
|
-
|
301
|
-
logging.error(f"Error reading file for update {path}: {e}.")
|
302
|
-
self._files[path] = f"[Error: Could not read file at path '{path}': {e}]"
|
345
|
+
self._file_sources[path] = {'source': 'disk'}
|
303
346
|
self.mark_stale()
|
304
347
|
async def render(self) -> str:
|
305
348
|
if not self._files: return None
|
@@ -380,8 +423,16 @@ class Message(ABC):
|
|
380
423
|
processed_items.append(Images(url=image_url))
|
381
424
|
else:
|
382
425
|
raise ValueError(f"Unsupported item type in list: {item_type}")
|
426
|
+
elif isinstance(item, dict):
|
427
|
+
item_type = item.get('type')
|
428
|
+
if item_type == 'image_url':
|
429
|
+
image_url = item.get('image_url', {}).get('url')
|
430
|
+
if image_url:
|
431
|
+
processed_items.append(Images(url=image_url))
|
432
|
+
else:
|
433
|
+
raise ValueError(f"Unsupported dict item type: {item_type}")
|
383
434
|
else:
|
384
|
-
raise TypeError(f"Unsupported item type: {type(item)}. Must be str, ContextProvider, or
|
435
|
+
raise TypeError(f"Unsupported item type: {type(item)}. Must be str, ContextProvider, list, or dict.")
|
385
436
|
self._items: List[ContextProvider] = processed_items
|
386
437
|
self._parent_messages: Optional['Messages'] = None
|
387
438
|
|
@@ -1783,6 +1783,133 @@ Files: {Files(visible=True, name="files")}
|
|
1783
1783
|
self.assertEqual(len(rendered_separated), 1, "被空消息隔开的同角色消息在渲染时应该合并")
|
1784
1784
|
self.assertEqual(rendered_separated[0]['content'], "hihi2", "被空消息隔开的同角色消息合并后内容不正确")
|
1785
1785
|
|
1786
|
+
async def test_zzc_files_update_with_head(self):
|
1787
|
+
"""测试 Files.update 是否支持 head 参数以及新的 refresh 逻辑"""
|
1788
|
+
test_file = "test_file_head.txt"
|
1789
|
+
file_content = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
|
1790
|
+
with open(test_file, "w", encoding='utf-8') as f:
|
1791
|
+
f.write(file_content)
|
1792
|
+
|
1793
|
+
try:
|
1794
|
+
messages = Messages(UserMessage(Files(name="files")))
|
1795
|
+
|
1796
|
+
# 1. Test update with head=3 using the provider from Messages
|
1797
|
+
files_provider = messages.provider("files")
|
1798
|
+
self.assertIsInstance(files_provider, Files)
|
1799
|
+
files_provider.update(path=test_file, head=3)
|
1800
|
+
|
1801
|
+
rendered_head = await messages.render_latest()
|
1802
|
+
expected_head_content = "Line 1\nLine 2\nLine 3"
|
1803
|
+
self.assertIn(f"<file_content>{expected_head_content}</file_content>", rendered_head[0]['content'])
|
1804
|
+
self.assertNotIn("Line 4", rendered_head[0]['content'])
|
1805
|
+
|
1806
|
+
# 2. Test update without head (default behavior)
|
1807
|
+
files_provider.update(path=test_file)
|
1808
|
+
rendered_full = await messages.render_latest()
|
1809
|
+
self.assertIn(f"<file_content>{file_content}</file_content>", rendered_full[0]['content'])
|
1810
|
+
|
1811
|
+
# 3. Test that refresh overwrites manual content when file exists
|
1812
|
+
files_provider.update(path=test_file, content="manual content")
|
1813
|
+
rendered_overwritten = await messages.render_latest()
|
1814
|
+
self.assertIn(f"<file_content>{file_content}</file_content>", rendered_overwritten[0]['content'])
|
1815
|
+
self.assertNotIn("manual content", rendered_overwritten[0]['content'])
|
1816
|
+
|
1817
|
+
# 4. Test that manual content is kept if file does not exist
|
1818
|
+
non_existent_file = "non_existent_for_sure.txt"
|
1819
|
+
files_provider.update(path=non_existent_file, content="manual content to keep")
|
1820
|
+
rendered_kept = await messages.render_latest()
|
1821
|
+
self.assertIn("<file_content>manual content to keep</file_content>", rendered_kept[0]['content'])
|
1822
|
+
|
1823
|
+
finally:
|
1824
|
+
if os.path.exists(test_file):
|
1825
|
+
os.remove(test_file)
|
1826
|
+
|
1827
|
+
async def test_zzd_files_update_with_string_head(self):
|
1828
|
+
"""测试 Files.update 是否能自动将字符串 head 参数转换为整数"""
|
1829
|
+
test_file = "test_file_string_head.txt"
|
1830
|
+
file_content = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5"
|
1831
|
+
with open(test_file, "w", encoding='utf-8') as f:
|
1832
|
+
f.write(file_content)
|
1833
|
+
|
1834
|
+
try:
|
1835
|
+
messages = Messages(UserMessage(Files(name="files")))
|
1836
|
+
|
1837
|
+
# Test update with head="3"
|
1838
|
+
files_provider = messages.provider("files")
|
1839
|
+
self.assertIsInstance(files_provider, Files)
|
1840
|
+
# This is the part that will likely fail before implementation
|
1841
|
+
files_provider.update(path=test_file, head="3")
|
1842
|
+
|
1843
|
+
rendered_head = await messages.render_latest()
|
1844
|
+
expected_head_content = "Line 1\nLine 2\nLine 3"
|
1845
|
+
self.assertIn(f"<file_content>{expected_head_content}</file_content>", rendered_head[0]['content'])
|
1846
|
+
self.assertNotIn("Line 4", rendered_head[0]['content'])
|
1847
|
+
|
1848
|
+
finally:
|
1849
|
+
if os.path.exists(test_file):
|
1850
|
+
os.remove(test_file)
|
1851
|
+
|
1852
|
+
async def test_zzc_files_update_with_head_and_content(self):
|
1853
|
+
"""测试update同时设置path, content, head时的行为"""
|
1854
|
+
existing_file = "test_file_existing.txt"
|
1855
|
+
non_existent_file = "test_file_non_existent.txt"
|
1856
|
+
existing_content = "Line 1 (from file)\nLine 2 (from file)\nLine 3 (from file)\nLine 4 (from file)"
|
1857
|
+
manual_content = "Line 1 (manual)\nLine 2 (manual)\nLine 3 (manual)\nLine 4 (manual)"
|
1858
|
+
|
1859
|
+
# --- 场景1: 文件存在 ---
|
1860
|
+
with open(existing_file, "w", encoding='utf-8') as f:
|
1861
|
+
f.write(existing_content)
|
1862
|
+
try:
|
1863
|
+
messages = Messages(UserMessage(Files(name="files")))
|
1864
|
+
files_provider = messages.provider("files")
|
1865
|
+
files_provider.update(path=existing_file, content=manual_content, head=2)
|
1866
|
+
rendered_existing = await messages.render_latest()
|
1867
|
+
expected_existing_head = "Line 1 (from file)\nLine 2 (from file)"
|
1868
|
+
self.assertIn(f"<file_content>{expected_existing_head}</file_content>", rendered_existing[0]['content'])
|
1869
|
+
self.assertNotIn("manual", rendered_existing[0]['content'])
|
1870
|
+
finally:
|
1871
|
+
if os.path.exists(existing_file):
|
1872
|
+
os.remove(existing_file)
|
1873
|
+
|
1874
|
+
# --- 场景2: 文件不存在 ---
|
1875
|
+
try:
|
1876
|
+
messages = Messages(UserMessage(Files(name="files")))
|
1877
|
+
files_provider = messages.provider("files")
|
1878
|
+
files_provider.update(path=non_existent_file, content=manual_content, head=2)
|
1879
|
+
rendered_non_existent = await messages.render_latest()
|
1880
|
+
expected_manual_head = "Line 1 (manual)\nLine 2 (manual)"
|
1881
|
+
self.assertIn(f"<file_content>{expected_manual_head}</file_content>", rendered_non_existent[0]['content'])
|
1882
|
+
self.assertNotIn("from file", rendered_non_existent[0]['content'])
|
1883
|
+
finally:
|
1884
|
+
if os.path.exists(non_existent_file):
|
1885
|
+
os.remove(non_existent_file)
|
1886
|
+
|
1887
|
+
async def test_auto_convert_image_dict_to_image_provider(self):
|
1888
|
+
"""测试 UserMessage 是否能自动转换 image_url 字典为 Images provider"""
|
1889
|
+
base64_image_data = "/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9/KKKKAP/2Q=="
|
1890
|
+
image_dict = {
|
1891
|
+
'type': 'image_url',
|
1892
|
+
'image_url': {
|
1893
|
+
'url': f'data:image/jpeg;base64,{base64_image_data}'
|
1894
|
+
}
|
1895
|
+
}
|
1896
|
+
|
1897
|
+
# This is the functionality we are testing.
|
1898
|
+
# The UserMessage should be able to take this dictionary directly.
|
1899
|
+
message = UserMessage(image_dict)
|
1900
|
+
|
1901
|
+
# 1. Verify that the message contains exactly one provider.
|
1902
|
+
self.assertEqual(len(message.provider()), 1)
|
1903
|
+
|
1904
|
+
# 2. Verify that the provider is an instance of the Images class.
|
1905
|
+
image_provider = message.provider()[0]
|
1906
|
+
self.assertIsInstance(image_provider, Images)
|
1907
|
+
|
1908
|
+
# 3. Verify that the content of the Images provider is correct.
|
1909
|
+
rendered = await message.render_latest()
|
1910
|
+
self.assertEqual(rendered['content'][0]['image_url']['url'], image_dict['image_url']['url'])
|
1911
|
+
|
1912
|
+
|
1786
1913
|
# ==============================================================================
|
1787
1914
|
# 6. 演示
|
1788
1915
|
# ==============================================================================
|
@@ -295,7 +295,7 @@ async def get_gemini_payload(request, engine, provider, api_key=None):
|
|
295
295
|
if key == request.model:
|
296
296
|
for k, v in value.items():
|
297
297
|
payload[k] = v
|
298
|
-
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key:
|
298
|
+
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key and " " not in key:
|
299
299
|
payload[key] = value
|
300
300
|
|
301
301
|
return url, headers, payload
|
@@ -591,7 +591,7 @@ async def get_vertex_gemini_payload(request, engine, provider, api_key=None):
|
|
591
591
|
if key == request.model:
|
592
592
|
for k, v in value.items():
|
593
593
|
payload[k] = v
|
594
|
-
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key:
|
594
|
+
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key and " " not in key:
|
595
595
|
payload[key] = value
|
596
596
|
|
597
597
|
return url, headers, payload
|
@@ -1116,6 +1116,9 @@ async def get_gpt_payload(request, engine, provider, api_key=None):
|
|
1116
1116
|
if "temperature" in payload:
|
1117
1117
|
payload.pop("temperature")
|
1118
1118
|
|
1119
|
+
if "v1/responses" in url:
|
1120
|
+
payload.pop("stream_options", None)
|
1121
|
+
|
1119
1122
|
# 代码生成/数学解题 0.0
|
1120
1123
|
# 数据抽取/分析 1.0
|
1121
1124
|
# 通用对话 1.3
|
@@ -1149,7 +1152,7 @@ async def get_gpt_payload(request, engine, provider, api_key=None):
|
|
1149
1152
|
if key == request.model:
|
1150
1153
|
for k, v in value.items():
|
1151
1154
|
payload[k] = v
|
1152
|
-
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key:
|
1155
|
+
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key and " " not in key:
|
1153
1156
|
payload[key] = value
|
1154
1157
|
|
1155
1158
|
return url, headers, payload
|
@@ -1247,7 +1250,7 @@ async def get_azure_payload(request, engine, provider, api_key=None):
|
|
1247
1250
|
if key == request.model:
|
1248
1251
|
for k, v in value.items():
|
1249
1252
|
payload[k] = v
|
1250
|
-
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key:
|
1253
|
+
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key and " " not in key:
|
1251
1254
|
payload[key] = value
|
1252
1255
|
|
1253
1256
|
return url, headers, payload
|
@@ -1367,7 +1370,7 @@ async def get_azure_databricks_payload(request, engine, provider, api_key=None):
|
|
1367
1370
|
if key == request.model:
|
1368
1371
|
for k, v in value.items():
|
1369
1372
|
payload[k] = v
|
1370
|
-
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key:
|
1373
|
+
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key and " " not in key:
|
1371
1374
|
payload[key] = value
|
1372
1375
|
|
1373
1376
|
return url, headers, payload
|
@@ -1454,7 +1457,7 @@ async def get_openrouter_payload(request, engine, provider, api_key=None):
|
|
1454
1457
|
if key == request.model:
|
1455
1458
|
for k, v in value.items():
|
1456
1459
|
payload[k] = v
|
1457
|
-
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key:
|
1460
|
+
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key and " " not in key:
|
1458
1461
|
payload[key] = value
|
1459
1462
|
|
1460
1463
|
return url, headers, payload
|
@@ -1820,7 +1823,7 @@ async def get_claude_payload(request, engine, provider, api_key=None):
|
|
1820
1823
|
if key == request.model:
|
1821
1824
|
for k, v in value.items():
|
1822
1825
|
payload[k] = v
|
1823
|
-
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key:
|
1826
|
+
elif all(_model not in request.model.lower() for _model in model_dict.keys()) and "-" not in key and " " not in key:
|
1824
1827
|
payload[key] = value
|
1825
1828
|
|
1826
1829
|
return url, headers, payload
|
@@ -52,7 +52,7 @@ class BaseAPI:
|
|
52
52
|
self.v1_models: str = urlunparse(parsed_url[:2] + (before_v1 + "models",) + ("",) * 3)
|
53
53
|
|
54
54
|
if "v1/responses" in parsed_url.path:
|
55
|
-
self.chat_url: str =
|
55
|
+
self.chat_url: str = api_url
|
56
56
|
else:
|
57
57
|
self.chat_url: str = urlunparse(parsed_url[:2] + (before_v1 + "chat/completions",) + ("",) * 3)
|
58
58
|
self.image_url: str = urlunparse(parsed_url[:2] + (before_v1 + "images/generations",) + ("",) * 3)
|
@@ -89,6 +89,13 @@ class RepetitiveResponseError(Exception):
|
|
89
89
|
self.count = count
|
90
90
|
|
91
91
|
|
92
|
+
class AllToolsMissingParametersError(Exception):
|
93
|
+
"""Custom exception for when all tools are missing required parameters."""
|
94
|
+
def __init__(self, message, response_text):
|
95
|
+
super().__init__(message)
|
96
|
+
self.response_text = response_text
|
97
|
+
|
98
|
+
|
92
99
|
class chatgpt(BaseLLM):
|
93
100
|
"""
|
94
101
|
Official ChatGPT API
|
@@ -500,6 +507,9 @@ class chatgpt(BaseLLM):
|
|
500
507
|
missing_required_params.append(f"Error: {tool_name} missing required parameters: {missing_required_params}")
|
501
508
|
function_parameter = valid_function_parameters
|
502
509
|
|
510
|
+
if not function_parameter and missing_required_params:
|
511
|
+
raise AllToolsMissingParametersError("\n\n".join(missing_required_params), response_text=full_response)
|
512
|
+
|
503
513
|
# 删除 task_complete 跟其他工具一起调用的情况,因为 task_complete 必须单独调用
|
504
514
|
if len(function_parameter) > 1:
|
505
515
|
function_parameter = [tool_dict for tool_dict in function_parameter if tool_dict.get("function_name", "") != "task_complete"]
|
@@ -615,7 +625,7 @@ class chatgpt(BaseLLM):
|
|
615
625
|
final_tool_response = tool_response
|
616
626
|
if "<tool_error>" not in tool_response:
|
617
627
|
if tool_name == "read_file":
|
618
|
-
self.conversation[convo_id].provider("files").update(tool_info['parameter']["file_path"], tool_response)
|
628
|
+
self.conversation[convo_id].provider("files").update(tool_info['parameter']["file_path"], tool_response, head=safe_get(tool_info, 'parameter', "head", default=None))
|
619
629
|
final_tool_response = "Read file successfully! The file content has been updated in the tag <latest_file_content>."
|
620
630
|
elif tool_name == "get_knowledge_graph_tree":
|
621
631
|
self.conversation[convo_id].provider("knowledge_graph").visible = True
|
@@ -810,6 +820,13 @@ class chatgpt(BaseLLM):
|
|
810
820
|
{"role": "user", "content": "你的消息没有以[done]结尾,请重新输出"}
|
811
821
|
]
|
812
822
|
continue
|
823
|
+
except AllToolsMissingParametersError as e:
|
824
|
+
self.logger.warning(f"All tools are missing required parameters: {e}. Retrying with corrective prompt.")
|
825
|
+
need_done_prompt = [
|
826
|
+
{"role": "assistant", "content": e.response_text},
|
827
|
+
{"role": "user", "content": f"{e.message},请重新输出"}
|
828
|
+
]
|
829
|
+
continue
|
813
830
|
except EmptyResponseError as e:
|
814
831
|
self.logger.warning(f"{e}, retrying...")
|
815
832
|
continue
|
@@ -2,7 +2,6 @@ import os
|
|
2
2
|
import json
|
3
3
|
import chardet
|
4
4
|
from pdfminer.high_level import extract_text
|
5
|
-
|
6
5
|
from ..aient.aient.plugins import register_tool
|
7
6
|
from ..core import current_work_dir
|
8
7
|
|
@@ -52,9 +51,7 @@ Examples:
|
|
52
51
|
</read_file>
|
53
52
|
"""
|
54
53
|
|
55
|
-
work_dir = current_work_dir.get()
|
56
|
-
if not work_dir:
|
57
|
-
return f"<tool_error>关键上下文 'current_work_dir' 未设置,无法确定安全的工作目录。</tool_error>"
|
54
|
+
work_dir = current_work_dir.get(os.getcwd())
|
58
55
|
|
59
56
|
# Determine the final, absolute path for the file operation.
|
60
57
|
if os.path.isabs(file_path):
|
@@ -62,12 +59,7 @@ Examples:
|
|
62
59
|
else:
|
63
60
|
final_path = os.path.join(work_dir, file_path)
|
64
61
|
|
65
|
-
# Security check: Ensure the final path is within the designated work directory.
|
66
|
-
# abs_work_dir = os.path.abspath(work_dir)
|
67
62
|
abs_final_path = os.path.abspath(final_path)
|
68
|
-
# if not abs_final_path.startswith(abs_work_dir):
|
69
|
-
# return f"<tool_error>路径遍历攻击被阻止。尝试写入的路径 '{file_path}' 解析后超出了允许的工作目录范围 '{abs_work_dir}'。</tool_error>"
|
70
|
-
|
71
63
|
file_path = abs_final_path
|
72
64
|
|
73
65
|
try:
|
@@ -125,16 +117,23 @@ Examples:
|
|
125
117
|
# 更新:修改通用文件读取逻辑以支持多种编码
|
126
118
|
# 这部分替换了原有的 else 块内容
|
127
119
|
try:
|
120
|
+
# 优化:分块读取以加速 chardet
|
128
121
|
with open(file_path, 'rb') as file: # 以二进制模式读取
|
129
|
-
|
122
|
+
# 1. 读取用于检测编码的初始块
|
123
|
+
detection_chunk = file.read(4096) # Read first 4KB
|
124
|
+
# 2. 读取文件的剩余部分
|
125
|
+
remaining_data = file.read()
|
126
|
+
|
127
|
+
# 将两部分数据合并为完整的文件内容
|
128
|
+
raw_data = detection_chunk + remaining_data
|
130
129
|
|
131
130
|
if not raw_data: # 处理空文件
|
132
131
|
text_content = ""
|
133
132
|
else:
|
134
|
-
|
133
|
+
# 3. 仅对初始块运行 chardet 以提高速度
|
134
|
+
detected_info = chardet.detect(detection_chunk) # Detect on the small chunk
|
135
135
|
primary_encoding_to_try = detected_info['encoding']
|
136
136
|
confidence = detected_info['confidence']
|
137
|
-
|
138
137
|
decoded_successfully = False
|
139
138
|
|
140
139
|
# 尝试1: 使用检测到的编码 (如果置信度高且编码有效)
|
@@ -193,14 +192,6 @@ Examples:
|
|
193
192
|
# Invalid head value, ignore and proceed with normal logic.
|
194
193
|
pass
|
195
194
|
|
196
|
-
# if file_path.lower().endswith('.csv'):
|
197
|
-
# lines = text_content.splitlines(True)
|
198
|
-
# if len(lines) > 500:
|
199
|
-
# top_lines = lines[:250]
|
200
|
-
# bottom_lines = lines[-250:]
|
201
|
-
# omitted_count = len(lines) - 500
|
202
|
-
# text_content = "".join(top_lines) + f"\n... (中间省略了 {omitted_count} 行) ...\n" + "".join(bottom_lines)
|
203
|
-
|
204
195
|
# 返回文件内容
|
205
196
|
return text_content
|
206
197
|
|
@@ -213,7 +204,8 @@ Examples:
|
|
213
204
|
return f"<tool_error>读取文件时发生错误: {e}</tool_error>"
|
214
205
|
|
215
206
|
if __name__ == "__main__":
|
216
|
-
# python -m beswarm.
|
217
|
-
result = read_file("
|
207
|
+
# python -m beswarm.tools.read_file
|
208
|
+
# result = read_file("traindata.csv", head=3)
|
209
|
+
result = read_file("testdata.csv", head=3)
|
218
210
|
print(result)
|
219
|
-
print(len(result))
|
211
|
+
print(f"行数: {len(result.splitlines())}")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/arduino-tags.scm
RENAMED
File without changes
|
File without changes
|
{beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/chatito-tags.scm
RENAMED
File without changes
|
{beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/commonlisp-tags.scm
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/javascript-tags.scm
RENAMED
File without changes
|
File without changes
|
File without changes
|
{beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/properties-tags.scm
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{beswarm-0.3.12 → beswarm-0.3.14}/beswarm/queries/tree-sitter-language-pack/solidity-tags.scm
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|