flock-core 0.4.0b1__py3-none-any.whl → 0.4.0b3__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/core/flock.py CHANGED
@@ -23,6 +23,10 @@ from flock.core.context.context_manager import initialize_context
23
23
  from flock.core.execution.local_executor import run_local_workflow
24
24
  from flock.core.execution.temporal_executor import run_temporal_workflow
25
25
  from flock.core.logging.logging import LOGGERS, get_logger, get_module_loggers
26
+ from flock.core.serialization.serialization_utils import (
27
+ extract_pydantic_models_from_type_string,
28
+ )
29
+ from flock.core.util.input_resolver import split_top_level
26
30
 
27
31
  # Import FlockAgent using TYPE_CHECKING to avoid circular import at runtime
28
32
  if TYPE_CHECKING:
@@ -90,6 +94,14 @@ class Flock(BaseModel, Serializable):
90
94
  default=False,
91
95
  description="If True, execute workflows via Temporal; otherwise, run locally.",
92
96
  )
97
+ enable_logging: bool = Field(
98
+ default=False,
99
+ description="If True, enable logging for the Flock instance.",
100
+ )
101
+ show_flock_banner: bool = Field(
102
+ default=True,
103
+ description="If True, show the Flock banner.",
104
+ )
93
105
  # --- Runtime Attributes (Excluded from Serialization) ---
94
106
  # Store agents internally but don't make it part of the Pydantic model definition
95
107
  # Use a regular attribute, initialized in __init__
@@ -126,6 +138,8 @@ class Flock(BaseModel, Serializable):
126
138
  model=model,
127
139
  description=description,
128
140
  enable_temporal=enable_temporal,
141
+ enable_logging=enable_logging,
142
+ show_flock_banner=show_flock_banner,
129
143
  **kwargs, # Pass extra kwargs to Pydantic BaseModel
130
144
  )
131
145
 
@@ -275,30 +289,21 @@ class Flock(BaseModel, Serializable):
275
289
  # Check if an event loop is already running
276
290
  try:
277
291
  loop = asyncio.get_running_loop()
278
- # If we get here, an event loop is already running
279
- # Run directly in this loop using run_until_complete
280
- return loop.run_until_complete(
281
- self.run_async(
282
- start_agent=start_agent,
283
- input=input,
284
- context=context,
285
- run_id=run_id,
286
- box_result=box_result,
287
- agents=agents,
288
- )
289
- )
290
- except RuntimeError:
291
- # No running event loop, create a new one with asyncio.run
292
- return asyncio.run(
293
- self.run_async(
294
- start_agent=start_agent,
295
- input=input,
296
- context=context,
297
- run_id=run_id,
298
- box_result=box_result,
299
- agents=agents,
300
- )
292
+ except (
293
+ RuntimeError
294
+ ): # 'RuntimeError: There is no current event loop...'
295
+ loop = asyncio.new_event_loop()
296
+ asyncio.set_event_loop(loop)
297
+ return loop.run_until_complete(
298
+ self.run_async(
299
+ start_agent=start_agent,
300
+ input=input,
301
+ context=context,
302
+ run_id=run_id,
303
+ box_result=box_result,
304
+ agents=agents,
301
305
  )
306
+ )
302
307
 
303
308
  async def run_async(
304
309
  self,
@@ -476,6 +481,21 @@ class Flock(BaseModel, Serializable):
476
481
  agent_data = agent_instance.to_dict()
477
482
  data["agents"][name] = agent_data
478
483
 
484
+ if agent_instance.input:
485
+ logger.debug(
486
+ f"Extracting type information from agent '{name}' input: {agent_instance.input}"
487
+ )
488
+ input_types = self._extract_types_from_signature(
489
+ agent_instance.input
490
+ )
491
+ if input_types:
492
+ logger.debug(
493
+ f"Found input types in agent '{name}': {input_types}"
494
+ )
495
+ custom_types.update(
496
+ self._get_type_definitions(input_types)
497
+ )
498
+
479
499
  # Extract type information from agent outputs
480
500
  if agent_instance.output:
481
501
  logger.debug(
@@ -590,39 +610,49 @@ class Flock(BaseModel, Serializable):
590
610
  if not signature:
591
611
  return []
592
612
 
613
+ signature_parts = split_top_level(signature)
614
+
593
615
  # Basic type extraction - handles simple cases like "result: TypeName" or "list[TypeName]"
594
616
  custom_types = []
595
617
 
596
618
  # Look for type annotations (everything after ":")
597
- parts = signature.split(":")
598
- if len(parts) > 1:
599
- type_part = parts[1].strip()
600
-
601
- # Extract from list[Type]
602
- if "list[" in type_part:
603
- inner_type = type_part.split("list[")[1].split("]")[0].strip()
604
- if inner_type and inner_type.lower() not in [
605
- "str",
606
- "int",
607
- "float",
608
- "bool",
609
- "dict",
610
- "list",
611
- ]:
612
- custom_types.append(inner_type)
613
-
614
- # Extract direct type references
615
- elif type_part and type_part.lower() not in [
616
- "str",
617
- "int",
618
- "float",
619
- "bool",
620
- "dict",
621
- "list",
622
- ]:
623
- custom_types.append(
624
- type_part.split()[0]
625
- ) # Take the first word in case there's a description
619
+ for part in signature_parts:
620
+ parts = part.split(":")
621
+ if len(parts) > 1:
622
+ type_part = parts[1].strip()
623
+
624
+ pydantic_models = extract_pydantic_models_from_type_string(
625
+ type_part
626
+ )
627
+ if pydantic_models:
628
+ for model in pydantic_models:
629
+ custom_types.append(model.__name__)
630
+
631
+ # # Extract from list[Type]
632
+ # if "list[" in type_part:
633
+ # inner_type = type_part.split("list[")[1].split("]")[0].strip()
634
+ # if inner_type and inner_type.lower() not in [
635
+ # "str",
636
+ # "int",
637
+ # "float",
638
+ # "bool",
639
+ # "dict",
640
+ # "list",
641
+ # ]:
642
+ # custom_types.append(inner_type)
643
+
644
+ # # Extract direct type references
645
+ # elif type_part and type_part.lower() not in [
646
+ # "str",
647
+ # "int",
648
+ # "float",
649
+ # "bool",
650
+ # "dict",
651
+ # "list",
652
+ # ]:
653
+ # custom_types.append(
654
+ # type_part.split()[0]
655
+ # ) # Take the first word in case there's a description
626
656
 
627
657
  return custom_types
628
658
 
@@ -259,7 +259,7 @@ class FlockRegistry:
259
259
  f"Type '{type_name}' already registered. Overwriting."
260
260
  )
261
261
  self._types[type_name] = type_obj
262
- # logger.debug(f"Registered type: {type_name}")
262
+ logger.debug(f"Registered type: {type_name}")
263
263
  return type_name
264
264
  return None
265
265
 
@@ -87,7 +87,7 @@ class Serializable(ABC):
87
87
  # --- YAML Methods ---
88
88
  def to_yaml(
89
89
  self,
90
- path_type: Literal["absolute", "relative"],
90
+ path_type: Literal["absolute", "relative"] = "relative",
91
91
  sort_keys=False,
92
92
  default_flow_style=False,
93
93
  ) -> str:
@@ -144,7 +144,7 @@ class Serializable(ABC):
144
144
  def to_yaml_file(
145
145
  self,
146
146
  path: Path | str,
147
- path_type: Literal["absolute", "relative"] = "absolute",
147
+ path_type: Literal["absolute", "relative"] = "relative",
148
148
  **yaml_dump_kwargs,
149
149
  ) -> None:
150
150
  """Serialize to YAML file.
@@ -1,9 +1,12 @@
1
1
  # src/flock/core/serialization/serialization_utils.py
2
2
  """Utilities for recursive serialization/deserialization with callable handling."""
3
3
 
4
+ import ast
5
+ import builtins
4
6
  import importlib
7
+ import sys
5
8
  from collections.abc import Mapping, Sequence
6
- from typing import TYPE_CHECKING, Any
9
+ from typing import TYPE_CHECKING, Any, get_args, get_origin
7
10
 
8
11
  from pydantic import BaseModel
9
12
 
@@ -21,6 +24,98 @@ logger = get_logger("serialization.utils")
21
24
  # --- Serialization Helper ---
22
25
 
23
26
 
27
+ def extract_identifiers_from_type_str(type_str: str) -> set[str]:
28
+ """Extract all identifiers from a type annotation string using the AST."""
29
+ tree = ast.parse(type_str, mode="eval")
30
+ identifiers = set()
31
+
32
+ class IdentifierVisitor(ast.NodeVisitor):
33
+ def visit_Name(self, node):
34
+ identifiers.add(node.id)
35
+
36
+ def visit_Attribute(self, node):
37
+ # Optionally support dotted names like mymodule.MyModel
38
+ full_name = []
39
+ while isinstance(node, ast.Attribute):
40
+ full_name.append(node.attr)
41
+ node = node.value
42
+ if isinstance(node, ast.Name):
43
+ full_name.append(node.id)
44
+ identifiers.add(".".join(reversed(full_name)))
45
+
46
+ IdentifierVisitor().visit(tree)
47
+ return identifiers
48
+
49
+
50
+ def resolve_name(name: str):
51
+ """Resolve a name to a Python object from loaded modules."""
52
+ # Try dotted names first
53
+ parts = name.split(".")
54
+ obj = None
55
+
56
+ if len(parts) == 1:
57
+ # Search globals and builtins
58
+ if parts[0] in globals():
59
+ return globals()[parts[0]]
60
+ if parts[0] in builtins.__dict__:
61
+ return builtins.__dict__[parts[0]]
62
+ else:
63
+ try:
64
+ obj = sys.modules[parts[0]]
65
+ for part in parts[1:]:
66
+ obj = getattr(obj, part)
67
+ return obj
68
+ except Exception:
69
+ return None
70
+
71
+ # Try all loaded modules' symbols
72
+ for module in list(sys.modules.values()):
73
+ if module is None or not hasattr(module, "__dict__"):
74
+ continue
75
+ if parts[0] in module.__dict__:
76
+ return module.__dict__[parts[0]]
77
+
78
+ return None
79
+
80
+
81
+ def extract_pydantic_models_from_type_string(
82
+ type_str: str,
83
+ ) -> list[type[BaseModel]]:
84
+ identifiers = extract_identifiers_from_type_str(type_str)
85
+ models = []
86
+ for name in identifiers:
87
+ resolved = resolve_name(name)
88
+ if (
89
+ isinstance(resolved, type)
90
+ and issubclass(resolved, BaseModel)
91
+ and resolved is not BaseModel
92
+ ):
93
+ models.append(resolved)
94
+ return models
95
+
96
+
97
+ def collect_pydantic_models(
98
+ type_hint, seen: set[type[BaseModel]] | None = None
99
+ ) -> set[type[BaseModel]]:
100
+ if seen is None:
101
+ seen = set()
102
+
103
+ origin = get_origin(type_hint)
104
+ args = get_args(type_hint)
105
+
106
+ # Direct BaseModel
107
+ if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
108
+ seen.add(type_hint)
109
+ return seen
110
+
111
+ # For Unions, Lists, Dicts, Tuples, etc.
112
+ if origin is not None:
113
+ for arg in args:
114
+ collect_pydantic_models(arg, seen)
115
+
116
+ return seen
117
+
118
+
24
119
  def serialize_item(item: Any) -> Any:
25
120
  """Recursively prepares an item for serialization (e.g., to dict for YAML/JSON).
26
121
  Converts known callables to their path strings using FlockRegistry.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flock-core
3
- Version: 0.4.0b1
3
+ Version: 0.4.0b3
4
4
  Summary: Declarative LLM Orchestration at Scale
5
5
  Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
6
6
  License-File: LICENSE
@@ -16,12 +16,12 @@ flock/cli/view_results.py,sha256=dOzK0O1FHSIDERnx48y-2Xke9BkOHS7pcOhs64AyIg0,781
16
16
  flock/cli/yaml_editor.py,sha256=K3N0bh61G1TSDAZDnurqW9e_-hO6CtSQKXQqlDhCjVo,12527
17
17
  flock/cli/assets/release_notes.md,sha256=bqnk50jxM3w5uY44Dc7MkdT8XmRREFxrVBAG9XCOSSU,4896
18
18
  flock/core/__init__.py,sha256=R64A7zi51Uz6jzzCFWGol9SZQnMFz3ynEse5ivhSkEA,805
19
- flock/core/flock.py,sha256=aU3WXCDllLu6mJampQsSFNEwNkgat2M8BE3ITrTRhCo,61164
19
+ flock/core/flock.py,sha256=jKhI1UyrgPpel-r8Qjwp82Zwz2AghHMudKQYgy-8kYA,62329
20
20
  flock/core/flock_agent.py,sha256=L5sZ6weY4pNFbk8CEUrRD1506ojq_tx1jjQy02qEdC8,29126
21
21
  flock/core/flock_evaluator.py,sha256=dOXZeDOGZcAmJ9ahqq_2bdGUU1VOXY4skmwTVpAjiVw,1685
22
22
  flock/core/flock_factory.py,sha256=MGTkJCP1WGpV614f87r1vwe0tqAvBCoH9PlqtqDyJDk,2828
23
23
  flock/core/flock_module.py,sha256=96aFVYAgwpKN53xGbivQDUpikOYGFCxK5mqhclOcxY0,3003
24
- flock/core/flock_registry.py,sha256=CzJUH2i5MrjNKJUFV_fiTwd0BqySxa1uULnpCFpigps,20832
24
+ flock/core/flock_registry.py,sha256=ax2pIxv6hVqRw3eJCPQ9jAul_ocYWfl9BZttmu054d0,20830
25
25
  flock/core/flock_router.py,sha256=A5GaxcGvtiFlRLHBTW7okh5RDm3BdKam2uXvRHRaj7k,2187
26
26
  flock/core/api/__init__.py,sha256=OKlhzDWZJfA6ddBwxQUmATY0TSzESsH032u00iVGvdA,228
27
27
  flock/core/api/endpoints.py,sha256=m-QAyizCHGh3sd5IXaDEhquSxNgTZLpUVTXLdVjiDVw,9086
@@ -55,8 +55,8 @@ flock/core/serialization/__init__.py,sha256=CML7fPgG6p4c0CDBlJ_uwV1aZZhJKK9uy3Io
55
55
  flock/core/serialization/callable_registry.py,sha256=sUZECTZWsM3fJ8FDRQ-FgLNW9hF26nY17AD6fJKADMc,1419
56
56
  flock/core/serialization/json_encoder.py,sha256=gAKj2zU_8wQiNvdkby2hksSA4fbPNwTjup_yz1Le1Vw,1229
57
57
  flock/core/serialization/secure_serializer.py,sha256=n5-zRvvXddgJv1FFHsaQ2wuYdL3WUSGPvG_LGaffEJo,6144
58
- flock/core/serialization/serializable.py,sha256=mw1bIH64Ws5NIofrlqb72ELNWtnoxEQ2f54MuuFe_VE,12116
59
- flock/core/serialization/serialization_utils.py,sha256=YxlPoHFPtj5otWUlKEsKphWxnd_d6b8yMZN_5MDlzB4,7577
58
+ flock/core/serialization/serializable.py,sha256=qlv8TsTqRuklXiNuCMrvro5VKz764xC2i3FlgLJSkdk,12129
59
+ flock/core/serialization/serialization_utils.py,sha256=ONv7KUhc66e4sKYu9uFc3qU1jOg4DvIXRJ5QXaTIQj0,10298
60
60
  flock/core/tools/azure_tools.py,sha256=9Bi6IrB5pzBTBhBSxpCVMgx8HBud8nl4gDp8aN0NT6c,17031
61
61
  flock/core/tools/basic_tools.py,sha256=hEG14jNZ2itVvubCHTfsWkuJK6yuNwBtuFj2Js0VHZs,9043
62
62
  flock/core/tools/llm_tools.py,sha256=Bdt4Dpur5dGpxd2KFEQyxjfZazvW1HCDKY6ydMj6UgQ,21811
@@ -430,8 +430,8 @@ flock/workflow/activities.py,sha256=eVZDnxGJl_quNO-UTV3YgvTV8LrRaHN3QDAA1ANKzac,
430
430
  flock/workflow/agent_activities.py,sha256=NhBZscflEf2IMfSRa_pBM_TRP7uVEF_O0ROvWZ33eDc,963
431
431
  flock/workflow/temporal_setup.py,sha256=VWBgmBgfTBjwM5ruS_dVpA5AVxx6EZ7oFPGw4j3m0l0,1091
432
432
  flock/workflow/workflow.py,sha256=I9MryXW_bqYVTHx-nl2epbTqeRy27CAWHHA7ZZA0nAk,1696
433
- flock_core-0.4.0b1.dist-info/METADATA,sha256=R0xeR3s341pdXAXBvnicJlkMHcwDxUfGSMiIqtcI9h0,20773
434
- flock_core-0.4.0b1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
435
- flock_core-0.4.0b1.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
436
- flock_core-0.4.0b1.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
437
- flock_core-0.4.0b1.dist-info/RECORD,,
433
+ flock_core-0.4.0b3.dist-info/METADATA,sha256=1YKIfl_aB2pXjolghE3WcrQW8p6pJWsarwWc_zLZyDo,20773
434
+ flock_core-0.4.0b3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
435
+ flock_core-0.4.0b3.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
436
+ flock_core-0.4.0b3.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
437
+ flock_core-0.4.0b3.dist-info/RECORD,,