flock-core 0.3.40__py3-none-any.whl → 0.4.0b1__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 flock-core might be problematic. Click here for more details.
- flock/__init__.py +31 -0
- flock/cli/create_flock.py +58 -3
- flock/cli/load_flock.py +135 -1
- flock/cli/registry_management.py +367 -96
- flock/cli/yaml_editor.py +119 -6
- flock/core/__init__.py +13 -1
- flock/core/flock.py +865 -26
- flock/core/flock_agent.py +114 -22
- flock/core/flock_registry.py +36 -4
- flock/core/serialization/serializable.py +35 -8
- flock/core/util/cli_helper.py +2 -2
- flock/core/util/file_path_utils.py +223 -0
- {flock_core-0.3.40.dist-info → flock_core-0.4.0b1.dist-info}/METADATA +1 -1
- {flock_core-0.3.40.dist-info → flock_core-0.4.0b1.dist-info}/RECORD +17 -16
- {flock_core-0.3.40.dist-info → flock_core-0.4.0b1.dist-info}/WHEEL +0 -0
- {flock_core-0.3.40.dist-info → flock_core-0.4.0b1.dist-info}/entry_points.txt +0 -0
- {flock_core-0.3.40.dist-info → flock_core-0.4.0b1.dist-info}/licenses/LICENSE +0 -0
flock/cli/registry_management.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""Registry Management Module for the Flock CLI."""
|
|
2
2
|
|
|
3
|
+
import datetime
|
|
3
4
|
import importlib
|
|
4
5
|
import inspect
|
|
5
6
|
import os
|
|
7
|
+
from dataclasses import is_dataclass
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
from typing import Any
|
|
8
10
|
|
|
@@ -137,9 +139,21 @@ def display_registry_section(
|
|
|
137
139
|
table.add_column("Name/Path", style="cyan")
|
|
138
140
|
table.add_column("Type", style="green")
|
|
139
141
|
|
|
142
|
+
# Add file path column for components
|
|
143
|
+
if title == "Components":
|
|
144
|
+
table.add_column("File Path", style="yellow")
|
|
145
|
+
|
|
140
146
|
for name, item in filtered_items.items():
|
|
141
147
|
item_type = type(item).__name__
|
|
142
|
-
|
|
148
|
+
|
|
149
|
+
if title == "Components":
|
|
150
|
+
# Try to get the file path for component classes
|
|
151
|
+
file_path = (
|
|
152
|
+
inspect.getfile(item) if inspect.isclass(item) else "N/A"
|
|
153
|
+
)
|
|
154
|
+
table.add_row(name, item_type, file_path)
|
|
155
|
+
else:
|
|
156
|
+
table.add_row(name, item_type)
|
|
143
157
|
|
|
144
158
|
console.print(table)
|
|
145
159
|
console.print(f"Total: {len(filtered_items)} {title.lower()}")
|
|
@@ -154,11 +168,72 @@ def add_item_to_registry() -> None:
|
|
|
154
168
|
choices=["agent", "callable", "type", "component"],
|
|
155
169
|
).ask()
|
|
156
170
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
171
|
+
# For component types, offer file path option
|
|
172
|
+
use_file_path = False
|
|
173
|
+
if item_type == "component":
|
|
174
|
+
path_type = questionary.select(
|
|
175
|
+
"How do you want to specify the component?",
|
|
176
|
+
choices=["Module Path", "File Path"],
|
|
177
|
+
).ask()
|
|
178
|
+
use_file_path = path_type == "File Path"
|
|
179
|
+
|
|
180
|
+
if use_file_path:
|
|
181
|
+
file_path = questionary.path(
|
|
182
|
+
"Enter the file path to the component:", only_directories=False
|
|
183
|
+
).ask()
|
|
184
|
+
|
|
185
|
+
if not file_path or not os.path.exists(file_path):
|
|
186
|
+
console.print(f"[red]Error: File {file_path} does not exist[/]")
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
module_name = questionary.text(
|
|
190
|
+
"Enter the component class name in the file:"
|
|
191
|
+
).ask()
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
# Use dynamic import to load the module from file path
|
|
195
|
+
import importlib.util
|
|
160
196
|
|
|
161
|
-
|
|
197
|
+
spec = importlib.util.spec_from_file_location(
|
|
198
|
+
"temp_module", file_path
|
|
199
|
+
)
|
|
200
|
+
module = importlib.util.module_from_spec(spec)
|
|
201
|
+
spec.loader.exec_module(module)
|
|
202
|
+
|
|
203
|
+
if not hasattr(module, module_name):
|
|
204
|
+
console.print(
|
|
205
|
+
f"[red]Error: {module_name} not found in {file_path}[/]"
|
|
206
|
+
)
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
item = getattr(module, module_name)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
console.print(f"[red]Error importing from file: {e!s}[/]")
|
|
212
|
+
return False
|
|
213
|
+
else:
|
|
214
|
+
module_path = questionary.text(
|
|
215
|
+
"Enter the module path (e.g., 'your_module.submodule'):"
|
|
216
|
+
).ask()
|
|
217
|
+
|
|
218
|
+
item_name = questionary.text(
|
|
219
|
+
"Enter the item name within the module:"
|
|
220
|
+
).ask()
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
# Attempt to import the module
|
|
224
|
+
module = importlib.import_module(module_path)
|
|
225
|
+
|
|
226
|
+
# Get the item from the module
|
|
227
|
+
if not hasattr(module, item_name):
|
|
228
|
+
console.print(
|
|
229
|
+
f"[red]Error: {item_name} not found in {module_path}[/]"
|
|
230
|
+
)
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
item = getattr(module, item_name)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
console.print(f"[red]Error importing module: {e!s}[/]")
|
|
236
|
+
return False
|
|
162
237
|
|
|
163
238
|
alias = questionary.text(
|
|
164
239
|
"Enter an alias (optional, press Enter to skip):"
|
|
@@ -167,20 +242,8 @@ def add_item_to_registry() -> None:
|
|
|
167
242
|
if not alias:
|
|
168
243
|
alias = None
|
|
169
244
|
|
|
245
|
+
# Register the item based on its type
|
|
170
246
|
try:
|
|
171
|
-
# Attempt to import the module
|
|
172
|
-
module = importlib.import_module(module_path)
|
|
173
|
-
|
|
174
|
-
# Get the item from the module
|
|
175
|
-
if not hasattr(module, item_name):
|
|
176
|
-
console.print(
|
|
177
|
-
f"[red]Error: {item_name} not found in {module_path}[/]"
|
|
178
|
-
)
|
|
179
|
-
return False
|
|
180
|
-
|
|
181
|
-
item = getattr(module, item_name)
|
|
182
|
-
|
|
183
|
-
# Register the item based on its type
|
|
184
247
|
if item_type == "agent":
|
|
185
248
|
registry.register_agent(item)
|
|
186
249
|
console.print(
|
|
@@ -196,18 +259,19 @@ def add_item_to_registry() -> None:
|
|
|
196
259
|
console.print(f"[green]Successfully registered type: {result}[/]")
|
|
197
260
|
elif item_type == "component":
|
|
198
261
|
result = registry.register_component(item, alias)
|
|
262
|
+
# Store the file path information if we loaded from a file
|
|
263
|
+
if use_file_path and hasattr(registry, "_component_file_paths"):
|
|
264
|
+
# Check if the registry has component file paths attribute
|
|
265
|
+
# This will be added to registry in our update
|
|
266
|
+
registry._component_file_paths[result] = file_path
|
|
199
267
|
console.print(
|
|
200
268
|
f"[green]Successfully registered component: {result}[/]"
|
|
201
269
|
)
|
|
202
|
-
|
|
203
|
-
return True
|
|
204
|
-
|
|
205
|
-
except ImportError:
|
|
206
|
-
console.print(f"[red]Error: Could not import module {module_path}[/]")
|
|
207
270
|
except Exception as e:
|
|
208
|
-
console.print(f"[red]Error: {e!s}[/]")
|
|
271
|
+
console.print(f"[red]Error registering item: {e!s}[/]")
|
|
272
|
+
return False
|
|
209
273
|
|
|
210
|
-
return
|
|
274
|
+
return True
|
|
211
275
|
|
|
212
276
|
|
|
213
277
|
def remove_item_from_registry() -> None:
|
|
@@ -275,56 +339,123 @@ def remove_item_from_registry() -> None:
|
|
|
275
339
|
|
|
276
340
|
|
|
277
341
|
def auto_registration_scanner() -> None:
|
|
278
|
-
"""
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
"
|
|
282
|
-
|
|
342
|
+
"""Launch the auto-registration scanner interface."""
|
|
343
|
+
console.clear()
|
|
344
|
+
console.print(
|
|
345
|
+
Panel("[bold blue]Auto-Registration Scanner[/]"), justify="center"
|
|
346
|
+
)
|
|
347
|
+
console.line()
|
|
348
|
+
|
|
349
|
+
console.print(
|
|
350
|
+
"This utility will scan Python files for components, types, callables (tools), and agents that can be registered."
|
|
351
|
+
)
|
|
352
|
+
console.print(
|
|
353
|
+
"[yellow]Note: Registration is required for proper serialization and deserialization of your Flock.[/]"
|
|
354
|
+
)
|
|
355
|
+
console.line()
|
|
356
|
+
|
|
357
|
+
# Target directory selection
|
|
358
|
+
def path_filter(path):
|
|
359
|
+
"""Filter paths for selection."""
|
|
360
|
+
if os.path.isdir(path):
|
|
361
|
+
return True
|
|
362
|
+
return path.endswith(".py")
|
|
363
|
+
|
|
364
|
+
target_path = questionary.path(
|
|
365
|
+
"Select directory to scan:", file_filter=path_filter
|
|
283
366
|
).ask()
|
|
284
367
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
368
|
+
if not target_path or not os.path.exists(target_path):
|
|
369
|
+
console.print("[red]Invalid path selected. Aborting.[/]")
|
|
370
|
+
return
|
|
371
|
+
|
|
372
|
+
is_recursive = questionary.confirm(
|
|
373
|
+
"Scan recursively (include subdirectories)?", default=True
|
|
374
|
+
).ask()
|
|
292
375
|
|
|
293
|
-
# Ask if we should auto-register or just preview
|
|
294
376
|
auto_register = questionary.confirm(
|
|
295
|
-
"
|
|
296
|
-
default=False,
|
|
377
|
+
"Automatically register items found during scan?", default=True
|
|
297
378
|
).ask()
|
|
298
379
|
|
|
299
|
-
#
|
|
300
|
-
|
|
301
|
-
|
|
380
|
+
# Special callout for tools/callables
|
|
381
|
+
console.print(
|
|
382
|
+
"[bold blue]Tool Registration:[/] This scanner will look for functions that can be used as tools."
|
|
302
383
|
)
|
|
384
|
+
console.print(
|
|
385
|
+
"These will be registered as callables and can be properly serialized in your Flock YAML."
|
|
386
|
+
)
|
|
387
|
+
console.line()
|
|
388
|
+
|
|
389
|
+
with Progress(
|
|
390
|
+
SpinnerColumn(),
|
|
391
|
+
TextColumn("[bold blue]{task.description}"),
|
|
392
|
+
BarColumn(),
|
|
393
|
+
TextColumn("[bold green]{task.completed}/{task.total}"),
|
|
394
|
+
console=console,
|
|
395
|
+
) as progress:
|
|
396
|
+
task_id = progress.add_task(
|
|
397
|
+
"Scanning for registry items...", total=None
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
# Perform the scan
|
|
401
|
+
results = scan_for_registry_items(
|
|
402
|
+
target_path, recursive=is_recursive, auto_register=auto_register
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Mark task as complete
|
|
406
|
+
progress.update(task_id, completed=1, total=1)
|
|
407
|
+
console.line()
|
|
303
408
|
|
|
304
409
|
# Display results
|
|
305
|
-
console.print(
|
|
410
|
+
console.print("[bold green]Scan Complete![/]")
|
|
411
|
+
console.line()
|
|
412
|
+
|
|
413
|
+
total_found = sum(len(items) for items in results.values())
|
|
414
|
+
total_categories = sum(1 for items in results.values() if items)
|
|
415
|
+
|
|
416
|
+
console.print(
|
|
417
|
+
f"Found {total_found} items across {total_categories} categories."
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# Enhanced report section
|
|
421
|
+
table = Table(title="Scan Results")
|
|
422
|
+
table.add_column("Category", style="cyan")
|
|
423
|
+
table.add_column("Count", style="green")
|
|
424
|
+
table.add_column("Example Items", style="blue")
|
|
306
425
|
|
|
307
|
-
for category, items in
|
|
426
|
+
for category, items in results.items():
|
|
308
427
|
if items:
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
428
|
+
examples = ", ".join(items[:3])
|
|
429
|
+
if len(items) > 3:
|
|
430
|
+
examples += ", ..."
|
|
431
|
+
table.add_row(category, str(len(items)), examples)
|
|
432
|
+
else:
|
|
433
|
+
table.add_row(category, "0", "")
|
|
312
434
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
435
|
+
console.print(table)
|
|
436
|
+
console.line()
|
|
437
|
+
|
|
438
|
+
# Callout for tools and future serialization
|
|
439
|
+
if results.get("callables"):
|
|
440
|
+
console.print(
|
|
441
|
+
"[bold green]Note:[/] Found callable functions that can be used as tools."
|
|
442
|
+
)
|
|
443
|
+
console.print(
|
|
444
|
+
"These functions will now be properly serialized as callable references in your Flock YAML."
|
|
445
|
+
)
|
|
446
|
+
console.print(
|
|
447
|
+
"When sharing Flocks, ensure these callables are registered on the target system."
|
|
448
|
+
)
|
|
449
|
+
console.line()
|
|
450
|
+
|
|
451
|
+
# Show details options
|
|
452
|
+
if total_found > 0:
|
|
453
|
+
view_details = questionary.confirm(
|
|
454
|
+
"Would you like to view detailed results?", default=True
|
|
320
455
|
).ask()
|
|
321
456
|
|
|
322
|
-
if
|
|
323
|
-
#
|
|
324
|
-
scan_for_registry_items(target_path, recursive, True)
|
|
325
|
-
console.print(
|
|
326
|
-
"\n[green]Items have been registered to the registry.[/]"
|
|
327
|
-
)
|
|
457
|
+
if view_details:
|
|
458
|
+
view_registry_contents() # Show the registry contents after scan
|
|
328
459
|
|
|
329
460
|
|
|
330
461
|
def scan_for_registry_items(
|
|
@@ -517,8 +648,6 @@ def has_component_base(cls: type) -> bool:
|
|
|
517
648
|
def is_potential_type(cls: type) -> bool:
|
|
518
649
|
"""Check if a class is a Pydantic model or dataclass."""
|
|
519
650
|
try:
|
|
520
|
-
from dataclasses import is_dataclass
|
|
521
|
-
|
|
522
651
|
from pydantic import BaseModel
|
|
523
652
|
|
|
524
653
|
return issubclass(cls, BaseModel) or is_dataclass(cls)
|
|
@@ -559,59 +688,201 @@ def is_potential_registry_candidate(obj: Any) -> bool:
|
|
|
559
688
|
|
|
560
689
|
|
|
561
690
|
def export_registry() -> None:
|
|
562
|
-
"""Export
|
|
691
|
+
"""Export registry contents to a file."""
|
|
563
692
|
registry = get_registry()
|
|
564
693
|
|
|
565
|
-
#
|
|
694
|
+
# Select what to export
|
|
695
|
+
export_items = questionary.checkbox(
|
|
696
|
+
"Select what to export:",
|
|
697
|
+
choices=[
|
|
698
|
+
questionary.Choice("Agents", checked=True),
|
|
699
|
+
questionary.Choice("Callables (Tools)", checked=True),
|
|
700
|
+
questionary.Choice("Types", checked=True),
|
|
701
|
+
questionary.Choice("Components", checked=True),
|
|
702
|
+
questionary.Choice("File Paths", checked=True),
|
|
703
|
+
],
|
|
704
|
+
).ask()
|
|
705
|
+
|
|
706
|
+
if not export_items:
|
|
707
|
+
console.print("[yellow]No items selected for export.[/]")
|
|
708
|
+
return
|
|
709
|
+
|
|
710
|
+
# Select export format
|
|
566
711
|
export_format = questionary.select(
|
|
567
712
|
"Select export format:",
|
|
568
|
-
choices=["YAML", "JSON", "
|
|
713
|
+
choices=["YAML", "JSON", "Python"],
|
|
714
|
+
).ask()
|
|
715
|
+
|
|
716
|
+
# Select path type for serialization
|
|
717
|
+
path_type = questionary.select(
|
|
718
|
+
"How should file paths be formatted?",
|
|
719
|
+
choices=[
|
|
720
|
+
"absolute (full paths, best for local use)",
|
|
721
|
+
"relative (relative paths, better for sharing)",
|
|
722
|
+
],
|
|
723
|
+
default="absolute (full paths, best for local use)",
|
|
569
724
|
).ask()
|
|
570
725
|
|
|
571
|
-
#
|
|
572
|
-
|
|
573
|
-
|
|
726
|
+
# Extract just the first word
|
|
727
|
+
path_type = path_type.split()[0]
|
|
728
|
+
|
|
729
|
+
console.print(
|
|
730
|
+
f"\n[bold]Path type selected: [green]{path_type}[/green][/bold]"
|
|
731
|
+
)
|
|
732
|
+
if path_type == "relative":
|
|
733
|
+
console.print(
|
|
734
|
+
"Relative paths are recommended when sharing Flocks between systems.\n"
|
|
735
|
+
"They'll be converted to paths relative to the current directory."
|
|
736
|
+
)
|
|
737
|
+
else:
|
|
738
|
+
console.print(
|
|
739
|
+
"Absolute paths work best for local usage but may not work correctly\n"
|
|
740
|
+
"when sharing with others or moving files."
|
|
741
|
+
)
|
|
742
|
+
console.line()
|
|
743
|
+
|
|
744
|
+
# Get file path for export
|
|
745
|
+
file_path = questionary.path(
|
|
746
|
+
"Enter file path for export:",
|
|
574
747
|
default=f"flock_registry_export.{export_format.lower()}",
|
|
575
748
|
).ask()
|
|
576
749
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
750
|
+
if not file_path:
|
|
751
|
+
return
|
|
752
|
+
|
|
753
|
+
# Prepare export data
|
|
754
|
+
export_data = {}
|
|
755
|
+
|
|
756
|
+
if "Agents" in export_items:
|
|
757
|
+
export_data["agents"] = list(registry._agents.keys())
|
|
758
|
+
|
|
759
|
+
if "Callables (Tools)" in export_items:
|
|
760
|
+
export_data["callables"] = list(registry._callables.keys())
|
|
761
|
+
|
|
762
|
+
# Add serialization format information for tools
|
|
763
|
+
callable_details = {}
|
|
764
|
+
for callable_name in registry._callables.keys():
|
|
765
|
+
callable_obj = registry._callables[callable_name]
|
|
766
|
+
file_path_value = (
|
|
767
|
+
inspect.getfile(callable_obj)
|
|
768
|
+
if callable_obj and inspect.isfunction(callable_obj)
|
|
769
|
+
else "Unknown"
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
# Convert to relative path if needed
|
|
773
|
+
if path_type == "relative" and file_path_value != "Unknown":
|
|
774
|
+
try:
|
|
775
|
+
file_path_value = os.path.relpath(file_path_value)
|
|
776
|
+
except ValueError:
|
|
777
|
+
# Keep as absolute if can't make relative
|
|
778
|
+
pass
|
|
779
|
+
|
|
780
|
+
callable_details[callable_name] = {
|
|
781
|
+
"module": callable_obj.__module__,
|
|
782
|
+
"file": file_path_value,
|
|
783
|
+
"type": "function"
|
|
784
|
+
if inspect.isfunction(callable_obj)
|
|
785
|
+
else "other_callable",
|
|
786
|
+
}
|
|
787
|
+
export_data["callable_details"] = callable_details
|
|
788
|
+
|
|
789
|
+
if "Types" in export_items:
|
|
790
|
+
export_data["types"] = list(registry._types.keys())
|
|
791
|
+
|
|
792
|
+
if "Components" in export_items:
|
|
793
|
+
export_data["components"] = list(registry._components.keys())
|
|
794
|
+
|
|
795
|
+
# Include file paths if selected
|
|
796
|
+
if "File Paths" in export_items and hasattr(
|
|
797
|
+
registry, "_component_file_paths"
|
|
798
|
+
):
|
|
799
|
+
export_data["component_file_paths"] = {}
|
|
800
|
+
for component_name in registry._components.keys():
|
|
801
|
+
# Get the file path if available
|
|
802
|
+
if component_name in registry._component_file_paths:
|
|
803
|
+
file_path_value = registry._component_file_paths[
|
|
804
|
+
component_name
|
|
805
|
+
]
|
|
806
|
+
|
|
807
|
+
# Convert to relative path if needed
|
|
808
|
+
if path_type == "relative" and file_path_value:
|
|
809
|
+
try:
|
|
810
|
+
file_path_value = os.path.relpath(file_path_value)
|
|
811
|
+
except ValueError:
|
|
812
|
+
# Keep as absolute if can't make relative
|
|
813
|
+
pass
|
|
814
|
+
|
|
815
|
+
export_data["component_file_paths"][component_name] = (
|
|
816
|
+
file_path_value
|
|
817
|
+
)
|
|
584
818
|
|
|
819
|
+
# Add metadata about serialization format
|
|
820
|
+
export_data["metadata"] = {
|
|
821
|
+
"export_date": datetime.datetime.now().isoformat(),
|
|
822
|
+
"flock_version": "0.3.41", # Update with actual version
|
|
823
|
+
"serialization_format": {
|
|
824
|
+
"tools": "Callable reference names",
|
|
825
|
+
"components": "Module and class names",
|
|
826
|
+
"types": "Module and class names",
|
|
827
|
+
},
|
|
828
|
+
"path_type": path_type,
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
# Add serialization settings as a top-level element
|
|
832
|
+
export_data["serialization_settings"] = {"path_type": path_type}
|
|
833
|
+
|
|
834
|
+
try:
|
|
835
|
+
# Export the data
|
|
585
836
|
if export_format == "YAML":
|
|
586
837
|
import yaml
|
|
587
838
|
|
|
588
|
-
with open(
|
|
589
|
-
yaml.dump(export_data, f,
|
|
590
|
-
|
|
839
|
+
with open(file_path, "w") as f:
|
|
840
|
+
yaml.dump(export_data, f, default_flow_style=False)
|
|
591
841
|
elif export_format == "JSON":
|
|
592
842
|
import json
|
|
593
843
|
|
|
594
|
-
with open(
|
|
844
|
+
with open(file_path, "w") as f:
|
|
595
845
|
json.dump(export_data, f, indent=2)
|
|
846
|
+
elif export_format == "Python":
|
|
847
|
+
with open(file_path, "w") as f:
|
|
848
|
+
f.write("# Flock Registry Export\n")
|
|
849
|
+
f.write(f"# Generated on {datetime.datetime.now()}\n\n")
|
|
850
|
+
f.write("registry_data = ")
|
|
851
|
+
f.write(repr(export_data))
|
|
852
|
+
f.write("\n")
|
|
853
|
+
|
|
854
|
+
console.print(f"[green]Registry exported to {file_path}[/]")
|
|
855
|
+
console.print(f"[green]Paths formatted as: {path_type}[/]")
|
|
856
|
+
|
|
857
|
+
# Print information about tool serialization if tools were exported
|
|
858
|
+
if "Callables (Tools)" in export_items and registry._callables:
|
|
859
|
+
console.print("\n[bold blue]Tool Serialization Information:[/]")
|
|
860
|
+
console.print(
|
|
861
|
+
"Tools in Flock are now serialized as callable references rather than dictionaries."
|
|
862
|
+
)
|
|
863
|
+
console.print(
|
|
864
|
+
"This makes YAML files more readable and simplifies tool management."
|
|
865
|
+
)
|
|
866
|
+
console.print("When loading a Flock with tools:")
|
|
867
|
+
console.print(" 1. Tools must be registered in the registry")
|
|
868
|
+
console.print(" 2. The tools' modules must be importable")
|
|
869
|
+
console.print(
|
|
870
|
+
" 3. Tool functions have the same signature across systems"
|
|
871
|
+
)
|
|
596
872
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
)
|
|
607
|
-
for item in sorted(items):
|
|
608
|
-
f.write(f" - {item}\n")
|
|
609
|
-
f.write("\n")
|
|
610
|
-
|
|
611
|
-
console.print(f"[green]Registry exported to {export_path}[/]")
|
|
873
|
+
# Show example of how a tool would appear in YAML
|
|
874
|
+
if registry._callables:
|
|
875
|
+
console.print("\n[bold green]Example tool in YAML:[/]")
|
|
876
|
+
example_callable = next(iter(registry._callables.keys()))
|
|
877
|
+
console.print(
|
|
878
|
+
f" - {example_callable} # Function name reference"
|
|
879
|
+
)
|
|
880
|
+
console.print("instead of the old format:")
|
|
881
|
+
console.print(f" - __callable_ref__: {example_callable}")
|
|
612
882
|
|
|
613
883
|
except Exception as e:
|
|
614
|
-
console.print(f"[red]Error exporting registry: {e
|
|
884
|
+
console.print(f"[red]Error exporting registry: {e}[/]")
|
|
885
|
+
logger.error(f"Failed to export registry: {e}", exc_info=True)
|
|
615
886
|
|
|
616
887
|
|
|
617
888
|
if __name__ == "__main__":
|