flock-core 0.4.0b3__py3-none-any.whl → 0.4.0b5__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 CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  from rich.panel import Panel
4
4
 
5
+ from flock.cli.config import init_config_file, load_config_file
5
6
  from flock.cli.constants import (
7
+ CLI_CFG_FILE,
6
8
  CLI_EXIT,
7
9
  CLI_NOTES,
8
10
  CLI_REGISTRY_MANAGEMENT,
@@ -35,6 +37,16 @@ def main():
35
37
  # Show a welcome message on first run with the new tool serialization format
36
38
  import os
37
39
 
40
+ cfg_file = os.path.expanduser(f"~/.flock/{CLI_CFG_FILE}")
41
+ if not os.path.exists(cfg_file):
42
+ # Create the directory if it doesn't exist
43
+ os.makedirs(os.path.dirname(cfg_file), exist_ok=True)
44
+
45
+ init_config_file()
46
+ else:
47
+ # Load the config file
48
+ load_config_file()
49
+
38
50
  feature_flag_file = os.path.expanduser("~/.flock/tool_serialization_notice")
39
51
  if not os.path.exists(feature_flag_file):
40
52
  # Create the directory if it doesn't exist
flock/cli/config.py ADDED
@@ -0,0 +1,8 @@
1
+ def init_config_file():
2
+ """Initialize the config file."""
3
+ pass
4
+
5
+
6
+ def load_config_file():
7
+ """Load the config file."""
8
+ pass
flock/cli/constants.py CHANGED
@@ -1,5 +1,16 @@
1
1
  """Constants for the CLI module."""
2
2
 
3
+ CLI_CFG_FILE = ".env"
4
+ CLI_DEFAULT_ENV_VARS = {
5
+ "FLICK_API_KEY": "flock-api-key",
6
+ "FLICK_API_URL": "https://api.flock.com",
7
+ "FLICK_API_VERSION": "v1",
8
+ "FLICK_API_KEY": "flock-api-key",
9
+ "FLICK_API_URL": "https://api.flock.com",
10
+ "FLICK_API_VERSION": "v1",
11
+ }
12
+ CLI_DEFAULT_FOLDER = ".flock"
13
+
3
14
  CLI_CREATE_AGENT = "Create an agent"
4
15
  CLI_CREATE_FLOCK = "Create a new Flock"
5
16
  CLI_LOAD_AGENT = "Load an agent"
flock/cli/create_flock.py CHANGED
@@ -4,12 +4,14 @@ This module provides a wizard-like interface for creating new Flock instances,
4
4
  with options for basic configuration and initial agent creation.
5
5
  """
6
6
 
7
+ from datetime import datetime
7
8
  from pathlib import Path
8
9
 
9
10
  import questionary
10
11
  from rich.console import Console
11
12
  from rich.panel import Panel
12
13
 
14
+ from flock.cli.constants import CLI_DEFAULT_FOLDER
13
15
  from flock.cli.loaded_flock_cli import start_loaded_flock_cli
14
16
  from flock.core.flock import Flock
15
17
  from flock.core.flock_factory import FlockFactory
@@ -31,6 +33,11 @@ def create_flock():
31
33
  console.print("[bold]Step 1: Basic Flock Configuration[/]")
32
34
  console.line()
33
35
 
36
+ flock_name = questionary.text(
37
+ "Enter a name for this Flock:",
38
+ default="",
39
+ ).ask()
40
+
34
41
  # Get description
35
42
  description = questionary.text(
36
43
  "Enter a description for this Flock (optional):",
@@ -61,10 +68,11 @@ def create_flock():
61
68
  model = model_choice
62
69
 
63
70
  # Execution options
64
- enable_temporal = questionary.confirm(
65
- "Enable Temporal for distributed execution?",
66
- default=False,
67
- ).ask()
71
+ # enable_temporal = questionary.confirm(
72
+ # "Enable Temporal for distributed execution?",
73
+ # default=False,
74
+ # ).ask()
75
+ enable_temporal = False
68
76
 
69
77
  # Logging configuration
70
78
  enable_logging = questionary.confirm(
@@ -74,6 +82,7 @@ def create_flock():
74
82
 
75
83
  # Create the Flock instance
76
84
  flock = Flock(
85
+ name=flock_name,
77
86
  model=model,
78
87
  description=description,
79
88
  enable_temporal=enable_temporal,
@@ -200,7 +209,8 @@ def _save_flock_to_yaml(flock):
200
209
  flock: The Flock instance to save
201
210
  """
202
211
  # Get file path
203
- default_name = "my_flock.flock.yaml"
212
+ # default = flock.name + current date in 04_04_2025 format
213
+ default_name = f"{flock.name}_{datetime.now().strftime('%m_%d_%Y')}"
204
214
  file_path = questionary.text(
205
215
  "Enter file path to save Flock:",
206
216
  default=default_name,
@@ -208,7 +218,9 @@ def _save_flock_to_yaml(flock):
208
218
 
209
219
  # Ensure the file has the correct extension
210
220
  if not file_path.endswith((".yaml", ".yml")):
211
- file_path += ".yaml"
221
+ file_path += ".flock.yaml"
222
+
223
+ file_path = CLI_DEFAULT_FOLDER + "/" + file_path
212
224
 
213
225
  # Create directory if it doesn't exist
214
226
  save_path = Path(file_path)
@@ -1,10 +1,11 @@
1
1
  """Execute a Flock instance with a selected agent.
2
2
 
3
3
  This module provides functionality to execute a Flock instance with
4
- a selected agent and input configuration.
4
+ a selected agent and input configuration, including batch processing.
5
5
  """
6
6
 
7
7
  import json
8
+ import os
8
9
 
9
10
  import questionary
10
11
  from rich.console import Console
@@ -16,6 +17,15 @@ from flock.core.util.cli_helper import init_console
16
17
  # Create console instance
17
18
  console = Console()
18
19
 
20
+ # Try importing pandas for DataFrame support
21
+ try:
22
+ import pandas as pd
23
+
24
+ PANDAS_AVAILABLE = True
25
+ except ImportError:
26
+ pd = None
27
+ PANDAS_AVAILABLE = False
28
+
19
29
 
20
30
  def execute_flock(flock: Flock):
21
31
  """Execute a Flock instance.
@@ -198,3 +208,389 @@ def _parse_input_schema(input_schema: str) -> dict[str, dict[str, str]]:
198
208
  return {}
199
209
 
200
210
  return fields
211
+
212
+
213
+ def execute_flock_batch(flock: Flock):
214
+ """Execute a Flock instance in batch mode.
215
+
216
+ Args:
217
+ flock: The Flock instance to execute
218
+ """
219
+ if not flock:
220
+ console.print("[bold red]Error: No Flock instance provided.[/]")
221
+ return
222
+
223
+ agent_names = list(flock._agents.keys())
224
+
225
+ if not agent_names:
226
+ console.print("[yellow]No agents in this Flock to execute.[/]")
227
+ return
228
+
229
+ init_console()
230
+ console.print(
231
+ Panel("[bold green]Execute Flock - Batch Mode[/]"), justify="center"
232
+ )
233
+
234
+ # Step 1: Select start agent
235
+ console.print("\n[bold]Step 1: Select Start Agent[/]")
236
+
237
+ start_agent_name = questionary.select(
238
+ "Select an agent to start with:",
239
+ choices=agent_names,
240
+ ).ask()
241
+
242
+ if not start_agent_name:
243
+ return
244
+
245
+ start_agent = flock._agents[start_agent_name]
246
+
247
+ # Step 2: Configure batch input source
248
+ console.print("\n[bold]Step 2: Select Batch Input Source[/]")
249
+
250
+ if not PANDAS_AVAILABLE:
251
+ console.print(
252
+ "[yellow]Warning: pandas not available. CSV input/output functionality will be limited.[/]"
253
+ )
254
+
255
+ input_source_choices = ["Enter batch items manually"]
256
+
257
+ if PANDAS_AVAILABLE:
258
+ input_source_choices.insert(0, "Load from CSV file")
259
+
260
+ input_source = questionary.select(
261
+ "How would you like to provide batch inputs?",
262
+ choices=input_source_choices,
263
+ ).ask()
264
+
265
+ if not input_source:
266
+ return
267
+
268
+ batch_inputs = []
269
+ input_mapping = {}
270
+
271
+ if input_source == "Load from CSV file" and PANDAS_AVAILABLE:
272
+ # Ask for CSV file path
273
+ csv_path = questionary.path(
274
+ "Enter path to CSV file:",
275
+ ).ask()
276
+
277
+ if not csv_path:
278
+ return
279
+
280
+ try:
281
+ # Validate path exists
282
+ if not os.path.exists(csv_path):
283
+ console.print(
284
+ f"[bold red]Error: File '{csv_path}' does not exist.[/]"
285
+ )
286
+ return
287
+
288
+ # Preview CSV
289
+ df = pd.read_csv(csv_path)
290
+ console.print("\n[bold]CSV Preview (first 5 rows):[/]")
291
+ console.print(df.head().to_string())
292
+
293
+ # Configure column mapping
294
+ console.print("\n[bold]Configure Column Mapping:[/]")
295
+
296
+ # Parse input schema (if available)
297
+ input_fields = _parse_input_schema(start_agent.input)
298
+
299
+ # If we have input fields, map CSV columns to them
300
+ if input_fields:
301
+ columns = df.columns.tolist()
302
+
303
+ for field in input_fields.keys():
304
+ field_desc = input_fields[field].get("description", "")
305
+ prompt = f"Select column for '{field}'"
306
+
307
+ if field_desc:
308
+ prompt += f" ({field_desc})"
309
+
310
+ selected_col = questionary.select(
311
+ prompt,
312
+ choices=["(Skip this field)"] + columns,
313
+ ).ask()
314
+
315
+ if selected_col and selected_col != "(Skip this field)":
316
+ input_mapping[selected_col] = field
317
+ else:
318
+ # No schema, ask user to map columns manually
319
+ columns = df.columns.tolist()
320
+
321
+ for col in columns:
322
+ mapping = questionary.text(
323
+ f"Map column '{col}' to input field (leave empty to ignore):",
324
+ ).ask()
325
+
326
+ if mapping:
327
+ input_mapping[col] = mapping
328
+
329
+ if not input_mapping:
330
+ console.print("[yellow]Warning: No column mapping defined.[/]")
331
+ if not questionary.confirm(
332
+ "Continue without mapping?", default=False
333
+ ).ask():
334
+ return
335
+
336
+ # Use the CSV file path directly
337
+ batch_inputs = csv_path
338
+
339
+ except Exception as e:
340
+ console.print(f"[bold red]Error loading CSV: {e}[/]")
341
+ return
342
+
343
+ elif input_source == "Enter batch items manually":
344
+ # Parse input schema
345
+ input_fields = _parse_input_schema(start_agent.input)
346
+
347
+ if not input_fields:
348
+ console.print(
349
+ "[yellow]No input schema available. Using JSON input.[/]"
350
+ )
351
+
352
+ while True:
353
+ raw_input = questionary.text(
354
+ "Enter batch item as JSON (empty to finish):",
355
+ default="{}",
356
+ ).ask()
357
+
358
+ if not raw_input:
359
+ break
360
+
361
+ try:
362
+ item_data = json.loads(raw_input)
363
+ batch_inputs.append(item_data)
364
+ console.print(f"[green]Added item {len(batch_inputs)}[/]")
365
+ except json.JSONDecodeError:
366
+ console.print("[bold red]Error: Invalid JSON input.[/]")
367
+
368
+ else:
369
+ # We have input fields, ask for each field for each item
370
+ item_count = 1
371
+
372
+ while True:
373
+ console.print(f"\n[bold]Batch Item {item_count}[/]")
374
+
375
+ item_data = {}
376
+ for field, info in input_fields.items():
377
+ field_type = info.get("type", "str")
378
+ description = info.get("description", "")
379
+ prompt = f"Enter value for '{field}'"
380
+
381
+ if description:
382
+ prompt += f" ({description})"
383
+
384
+ prompt += " (empty to skip):"
385
+
386
+ value = questionary.text(prompt).ask()
387
+
388
+ if not value:
389
+ continue
390
+
391
+ # Convert value to appropriate type
392
+ if field_type == "int":
393
+ try:
394
+ value = int(value)
395
+ except ValueError:
396
+ console.print(
397
+ f"[yellow]Warning: Could not convert value to int, using as string.[/]"
398
+ )
399
+
400
+ item_data[field] = value
401
+
402
+ if item_data:
403
+ batch_inputs.append(item_data)
404
+ console.print(f"[green]Added item {len(batch_inputs)}[/]")
405
+
406
+ if not questionary.confirm(
407
+ "Add another batch item?",
408
+ default=len(batch_inputs)
409
+ < 2, # Default to yes if we have less than 2 items
410
+ ).ask():
411
+ break
412
+
413
+ item_count += 1
414
+
415
+ if isinstance(batch_inputs, list) and not batch_inputs:
416
+ console.print("[yellow]No batch items defined. Exiting.[/]")
417
+ return
418
+
419
+ # Step 3: Configure static inputs (if needed)
420
+ static_inputs = {}
421
+
422
+ if questionary.confirm(
423
+ "Would you like to add static inputs (common to all batch items)?",
424
+ default=False,
425
+ ).ask():
426
+ console.print("\n[bold]Configure Static Inputs[/]")
427
+
428
+ raw_static = questionary.text(
429
+ "Enter static inputs as JSON:",
430
+ default="{}",
431
+ ).ask()
432
+
433
+ try:
434
+ static_inputs = json.loads(raw_static)
435
+ except json.JSONDecodeError:
436
+ console.print(
437
+ "[bold red]Error: Invalid JSON for static inputs. Proceeding without static inputs.[/]"
438
+ )
439
+ static_inputs = {}
440
+
441
+ # Step 4: Configure batch execution options
442
+ console.print("\n[bold]Step 4: Configure Batch Execution Options[/]")
443
+
444
+ # Determine if we should use Temporal
445
+ use_temporal = False
446
+ # if questionary.confirm(
447
+ # f"Override Temporal setting? (Current: {flock.enable_temporal})",
448
+ # default=False,
449
+ # ).ask():
450
+ # use_temporal = questionary.confirm(
451
+ # "Use Temporal for batch execution?",
452
+ # default=flock.enable_temporal,
453
+ # ).ask()
454
+
455
+ # Configure parallelism
456
+ parallel = True
457
+ max_workers = 5
458
+
459
+ if not flock.enable_temporal if use_temporal is None else not use_temporal:
460
+ parallel = questionary.confirm(
461
+ "Run batch items in parallel?",
462
+ default=True,
463
+ ).ask()
464
+
465
+ if parallel:
466
+ max_workers_input = questionary.text(
467
+ "Maximum number of parallel workers:",
468
+ default="5",
469
+ ).ask()
470
+
471
+ try:
472
+ max_workers = int(max_workers_input)
473
+ except ValueError:
474
+ console.print(
475
+ "[yellow]Invalid worker count. Using default (5).[/]"
476
+ )
477
+ max_workers = 5
478
+
479
+ # Configure output options
480
+ silent_mode = questionary.confirm(
481
+ "Use silent mode with progress bar? (Recommended for large batches)",
482
+ default=True,
483
+ ).ask()
484
+
485
+ write_to_csv = None
486
+ if (
487
+ PANDAS_AVAILABLE
488
+ and questionary.confirm(
489
+ "Write results to CSV file?",
490
+ default=True,
491
+ ).ask()
492
+ ):
493
+ write_to_csv = questionary.text(
494
+ "CSV output path:",
495
+ default="batch_results.csv",
496
+ ).ask()
497
+
498
+ # Logging options
499
+ enable_logging = questionary.confirm(
500
+ "Enable detailed logging?",
501
+ default=False,
502
+ ).ask()
503
+
504
+ # Preview configuration
505
+ console.print("\n[bold]Batch Configuration Preview:[/]")
506
+ console.print(f"Agent: {start_agent_name}")
507
+
508
+ if isinstance(batch_inputs, str):
509
+ console.print(f"Input Source: CSV file ({batch_inputs})")
510
+ console.print(f"Column Mapping: {input_mapping}")
511
+ else:
512
+ console.print(f"Input Source: Manual entry ({len(batch_inputs)} items)")
513
+
514
+ if static_inputs:
515
+ console.print(f"Static Inputs: {json.dumps(static_inputs, indent=2)}")
516
+
517
+ # temporal_status = (
518
+ # "Default" if use_temporal is None else ("Yes" if use_temporal else "No")
519
+ # )
520
+ # console.print(f"Use Temporal: {temporal_status}")
521
+
522
+ if not (flock.enable_temporal if use_temporal is None else use_temporal):
523
+ console.print(f"Parallel Execution: {parallel}")
524
+ if parallel:
525
+ console.print(f"Max Workers: {max_workers}")
526
+
527
+ console.print(f"Silent Mode: {silent_mode}")
528
+
529
+ if write_to_csv:
530
+ console.print(f"Write Results to: {write_to_csv}")
531
+
532
+ # Confirm execution
533
+ confirm = questionary.confirm(
534
+ "Execute batch with this configuration?",
535
+ default=True,
536
+ ).ask()
537
+
538
+ if not confirm:
539
+ return
540
+
541
+ # Execute the batch
542
+ console.print("\n[bold]Executing Batch...[/]")
543
+
544
+ try:
545
+ # Handle logging settings
546
+ if enable_logging:
547
+ flock._configure_logging(True)
548
+
549
+ # Run the batch
550
+ results = flock.run_batch(
551
+ start_agent=start_agent_name,
552
+ batch_inputs=batch_inputs,
553
+ input_mapping=input_mapping or None,
554
+ static_inputs=static_inputs or None,
555
+ parallel=parallel,
556
+ max_workers=max_workers,
557
+ use_temporal=use_temporal,
558
+ box_results=True,
559
+ return_errors=True,
560
+ silent_mode=silent_mode,
561
+ write_to_csv=write_to_csv,
562
+ )
563
+
564
+ # Display results summary
565
+ console.print("\n[bold green]Batch Execution Complete![/]")
566
+
567
+ success_count = sum(1 for r in results if not isinstance(r, Exception))
568
+ error_count = sum(1 for r in results if isinstance(r, Exception))
569
+
570
+ console.print(f"Total Items: {len(results)}")
571
+ console.print(f"Successful: {success_count}")
572
+
573
+ if error_count > 0:
574
+ console.print(f"[bold red]Errors: {error_count}[/]")
575
+
576
+ # Ask if user wants to see detailed results
577
+ if questionary.confirm(
578
+ "View detailed results?",
579
+ default=False,
580
+ ).ask():
581
+ for i, result in enumerate(results):
582
+ console.print(f"\n[bold]Item {i + 1}:[/]")
583
+ if isinstance(result, Exception):
584
+ console.print(f"[bold red]Error: {result}[/]")
585
+ else:
586
+ # Display as formatted JSON
587
+ try:
588
+ console.print(json.dumps(result, indent=2))
589
+ except:
590
+ console.print(str(result))
591
+
592
+ if write_to_csv:
593
+ console.print(f"\n[green]Results written to: {write_to_csv}[/]")
594
+
595
+ except Exception as e:
596
+ console.print(f"\n[bold red]Error during batch execution:[/] {e!s}")
@@ -12,6 +12,7 @@ from flock.cli.constants import (
12
12
  CLI_REGISTRY_MANAGEMENT,
13
13
  CLI_SETTINGS,
14
14
  )
15
+ from flock.core.api import runner
15
16
  from flock.core.flock import Flock
16
17
  from flock.core.util.cli_helper import init_console
17
18
 
@@ -32,7 +33,7 @@ except ImportError:
32
33
  manage_agents_available = False
33
34
 
34
35
  try:
35
- from flock.cli.execute_flock import execute_flock
36
+ from flock.cli.execute_flock import execute_flock, execute_flock_batch
36
37
 
37
38
  execute_flock_available = True
38
39
  except ImportError:
@@ -94,6 +95,7 @@ def start_loaded_flock_cli(
94
95
  choices = [
95
96
  questionary.Separator(line=" "),
96
97
  "Execute Flock",
98
+ "Execute Flock - Batch Mode",
97
99
  "Start Web Server",
98
100
  "Start Web Server with UI",
99
101
  "Manage Agents",
@@ -131,6 +133,15 @@ def start_loaded_flock_cli(
131
133
  )
132
134
  input("\nPress Enter to continue...")
133
135
 
136
+ elif choice == "Execute Flock - Batch Mode":
137
+ if execute_flock_available:
138
+ execute_flock_batch(flock)
139
+ else:
140
+ console.print(
141
+ "[yellow]Batch execution functionality not yet implemented.[/]"
142
+ )
143
+ input("\nPress Enter to continue...")
144
+
134
145
  elif choice == "Start Web Server":
135
146
  _start_web_server(flock, create_ui=False)
136
147
 
@@ -203,7 +214,7 @@ def _start_web_server(flock: Flock, create_ui: bool = False) -> None:
203
214
  port = int(port_input)
204
215
 
205
216
  server_name_input = questionary.text(
206
- "Server name (default: Flock API):", default=server_name
217
+ "Server name (default: FlockName API):", default=flock.name + " API"
207
218
  ).ask()
208
219
  if server_name_input:
209
220
  server_name = server_name_input
@@ -214,6 +225,10 @@ def _start_web_server(flock: Flock, create_ui: bool = False) -> None:
214
225
  )
215
226
 
216
227
  # Use the Flock's start_api method
217
- flock.start_api(
218
- host=host, port=port, server_name=server_name, create_ui=create_ui
228
+ runner.start_flock_api(
229
+ flock=flock,
230
+ host=host,
231
+ port=port,
232
+ server_name=server_name,
233
+ create_ui=create_ui,
219
234
  )
flock/cli/runner.py ADDED
@@ -0,0 +1,41 @@
1
+ # src/flock/cli/runner.py
2
+ """Provides functionality to start the Flock CLI."""
3
+
4
+ from typing import TYPE_CHECKING
5
+
6
+ from flock.core.logging.logging import get_logger
7
+
8
+ if TYPE_CHECKING:
9
+ from flock.core.flock import Flock
10
+
11
+ logger = get_logger("cli.runner")
12
+
13
+
14
+ def start_flock_cli(
15
+ flock: Flock,
16
+ server_name: str = "Flock CLI",
17
+ show_results: bool = False,
18
+ edit_mode: bool = False,
19
+ ) -> None:
20
+ """Start a CLI interface for the given Flock instance."""
21
+ try:
22
+ # Import CLI function locally
23
+ from flock.cli.loaded_flock_cli import start_loaded_flock_cli
24
+ except ImportError:
25
+ logger.error(
26
+ "CLI components not found. Cannot start CLI. "
27
+ "Ensure the CLI modules are properly installed/available."
28
+ )
29
+ return
30
+
31
+ logger.info(
32
+ f"Starting CLI interface for loaded Flock instance '{flock.name}' ({len(flock.agents)} agents)"
33
+ )
34
+
35
+ # Pass the Flock instance to the CLI entry point
36
+ start_loaded_flock_cli(
37
+ flock=flock,
38
+ server_name=server_name,
39
+ show_results=show_results,
40
+ edit_mode=edit_mode,
41
+ )
flock/config.py CHANGED
@@ -1,8 +1,13 @@
1
1
  # flock/config.py
2
+ import os
3
+
2
4
  from decouple import config
3
5
 
4
6
  from flock.core.logging.telemetry import TelemetryConfig
5
7
 
8
+ cfg_file = os.path.expanduser(f"~/.flock/flock.cfg")
9
+
10
+
6
11
  # -- Connection and External Service Configurations --
7
12
  TEMPORAL_SERVER_URL = config("TEMPORAL_SERVER_URL", "localhost:7233")
8
13
  DEFAULT_MODEL = config("DEFAULT_MODEL", "openai/gpt-4o")