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 +82 -52
- flock/core/flock_registry.py +1 -1
- flock/core/serialization/serializable.py +2 -2
- flock/core/serialization/serialization_utils.py +96 -1
- {flock_core-0.4.0b1.dist-info → flock_core-0.4.0b3.dist-info}/METADATA +1 -1
- {flock_core-0.4.0b1.dist-info → flock_core-0.4.0b3.dist-info}/RECORD +9 -9
- {flock_core-0.4.0b1.dist-info → flock_core-0.4.0b3.dist-info}/WHEEL +0 -0
- {flock_core-0.4.0b1.dist-info → flock_core-0.4.0b3.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.0b1.dist-info → flock_core-0.4.0b3.dist-info}/licenses/LICENSE +0 -0
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
#
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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
|
|
flock/core/flock_registry.py
CHANGED
|
@@ -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
|
-
|
|
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"] = "
|
|
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.
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
59
|
-
flock/core/serialization/serialization_utils.py,sha256=
|
|
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.
|
|
434
|
-
flock_core-0.4.
|
|
435
|
-
flock_core-0.4.
|
|
436
|
-
flock_core-0.4.
|
|
437
|
-
flock_core-0.4.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|