flock-core 0.4.0b32__py3-none-any.whl → 0.4.0b34__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_agent.py CHANGED
@@ -231,13 +231,17 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
231
231
  agent=self.name,
232
232
  )
233
233
  try:
234
+ current_result = result
234
235
  for module in self.get_enabled_modules():
235
- await module.on_terminate(
236
- self, inputs, self.context, result
236
+ tmp_result = await module.on_terminate(
237
+ self, inputs, self.context, current_result
237
238
  )
239
+ # If the module returns a result, use it
240
+ if tmp_result:
241
+ current_result = tmp_result
238
242
 
239
243
  if self.write_to_file:
240
- self._save_output(self.name, result)
244
+ self._save_output(self.name, current_result)
241
245
 
242
246
  if self.wait_for_input:
243
247
  # simple input prompt
@@ -318,12 +322,15 @@ class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
318
322
  # Post-evaluate hooks
319
323
  current_result = result
320
324
  for module in self.get_enabled_modules():
321
- current_result = await module.on_post_evaluate(
325
+ tmp_result = await module.on_post_evaluate(
322
326
  self,
323
327
  current_inputs,
324
328
  self.context,
325
329
  current_result,
326
330
  )
331
+ # If the module returns a result, use it
332
+ if tmp_result:
333
+ current_result = tmp_result
327
334
 
328
335
  logger.debug(f"Evaluation completed for agent '{self.name}'")
329
336
  return current_result
@@ -76,7 +76,7 @@ class FlockModule(BaseModel, ABC):
76
76
  inputs: dict[str, Any],
77
77
  context: FlockContext | None = None,
78
78
  result: dict[str, Any] | None = None,
79
- ) -> dict[str, Any]:
79
+ ) -> dict[str, Any] | None:
80
80
  """Called after agent evaluation, can modify results."""
81
81
  return result
82
82
 
@@ -86,9 +86,9 @@ class FlockModule(BaseModel, ABC):
86
86
  inputs: dict[str, Any],
87
87
  context: FlockContext | None = None,
88
88
  result: dict[str, Any] | None = None,
89
- ) -> None:
89
+ ) -> dict[str, Any] | None:
90
90
  """Called when the agent finishes running."""
91
- pass
91
+ return result
92
92
 
93
93
  async def on_error(
94
94
  self,
@@ -62,9 +62,9 @@ def init_console(clear_screen: bool = True, show_banner: bool = True):
62
62
 
63
63
  if show_banner:
64
64
  console.print(banner_text)
65
- console.print(
66
- "[italic]'Magpie'[/] milestone - [bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n"
67
- )
65
+ console.print(
66
+ "[italic]'Magpie'[/] milestone - [bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n"
67
+ )
68
68
 
69
69
 
70
70
  def display_banner_no_version():
@@ -2,46 +2,37 @@ import re
2
2
 
3
3
 
4
4
  def split_top_level(spec: str) -> list[str]:
5
- """Split *spec* on commas that are truly between fields (i.e. that
6
- are **not** inside brackets, braces, parentheses, string literals,
7
- or the free-form description).
8
-
9
- Strategy
10
- --------
11
- 1. Single pass, char-by-char.
12
- 2. Track • nesting depth for (), [], {}
13
- • whether we are inside a quoted string
14
- 3. Consider a comma a separator *only* if depth == 0, we are not
15
- inside quotes **and** the text that follows matches
16
- `optional-ws + identifier + optional-ws + ':'`
17
- which can only be the start of the next field.
18
- """
5
+ """Return raw field strings, split on *top-level* commas."""
19
6
  fields: list[str] = []
20
7
  start = 0
21
8
  depth = 0
22
9
  quote_char: str | None = None
23
10
  i = 0
24
- ident_re = re.compile(r"[A-Za-z_]\w*") # cheap Python identifier
11
+ ident_re = re.compile(r"[A-Za-z_]\w*") # cheap identifier
25
12
 
26
13
  while i < len(spec):
27
14
  ch = spec[i]
28
15
 
29
- # ---------------- string handling ----------------
16
+ # ---------- string handling ----------
30
17
  if quote_char:
18
+ if ch == "\\":
19
+ i += 2 # skip escaped char
20
+ continue
31
21
  if ch == quote_char:
32
22
  quote_char = None
33
23
  i += 1
34
24
  continue
35
25
 
36
26
  if ch in {"'", '"'}:
37
- # treat as string delimiter only in places regular Python would
38
27
  prev = spec[i - 1] if i else " "
39
- if depth or prev.isspace() or prev in "=([{,:": # likely a quote
28
+ if (
29
+ depth or prev.isspace() or prev in "=([{,:"
30
+ ): # looks like a quote
40
31
  quote_char = ch
41
32
  i += 1
42
33
  continue
43
34
 
44
- # ---------------- bracket / brace / paren ----------------
35
+ # ---------- bracket / brace / paren ----------
45
36
  if ch in "([{":
46
37
  depth += 1
47
38
  i += 1
@@ -51,23 +42,28 @@ def split_top_level(spec: str) -> list[str]:
51
42
  i += 1
52
43
  continue
53
44
 
54
- # ---------------- field separators ----------------
45
+ # ---------- field separators ----------
55
46
  if ch == "," and depth == 0:
56
47
  j = i + 1
57
48
  while j < len(spec) and spec[j].isspace():
58
49
  j += 1
59
- if j < len(spec):
60
- id_match = ident_re.match(spec, j)
61
- if id_match:
62
- k = id_match.end()
63
- while k < len(spec) and spec[k].isspace():
64
- k += 1
65
- if k < len(spec) and spec[k] == ":":
66
- # confirmed: the comma ends the current field
67
- fields.append(spec[start:i].strip())
68
- start = i + 1
69
- i += 1
70
- continue
50
+ if j >= len(spec): # comma at end – split
51
+ fields.append(spec[start:i].strip())
52
+ start = i + 1
53
+ i += 1
54
+ continue
55
+
56
+ id_match = ident_re.match(spec, j)
57
+ if id_match:
58
+ k = id_match.end()
59
+ while k < len(spec) and spec[k].isspace():
60
+ k += 1
61
+ if k >= len(spec) or spec[k] in {":", "|", ","}:
62
+ # confirmed: comma separates two fields
63
+ fields.append(spec[start:i].strip())
64
+ start = i + 1
65
+ i += 1
66
+ continue
71
67
 
72
68
  i += 1
73
69
 
@@ -76,21 +72,29 @@ def split_top_level(spec: str) -> list[str]:
76
72
 
77
73
 
78
74
  def parse_schema(spec: str) -> list[tuple[str, str, str]]:
79
- """Turn the raw *spec* into List[(name, python_type, description)]."""
75
+ """Turn *spec* into a list of (name, python_type, description)."""
80
76
  result: list[tuple[str, str, str]] = []
81
77
 
82
78
  for field in split_top_level(spec):
83
- if ":" not in field:
84
- raise ValueError(f"Malformed field (missing ':'): {field!r}")
79
+ name = ""
80
+ type_str = "str" # default type
81
+ description = ""
85
82
 
86
- name_part, rest = field.split(":", 1)
87
- name = name_part.strip()
88
-
89
- type_part, _, desc_part = rest.partition("|")
90
- type_str = type_part.strip()
83
+ name_part, _, desc_part = field.partition("|")
91
84
  description = desc_part.strip()
85
+ main_part = name_part.strip()
86
+
87
+ if ":" in main_part:
88
+ name, type_candidate = main_part.split(":", 1)
89
+ name = name.strip()
90
+ type_candidate = type_candidate.strip()
91
+ if type_candidate:
92
+ type_str = type_candidate
93
+ else:
94
+ name = main_part # keeps default type
92
95
 
93
- result.append((name, type_str, description))
96
+ if name: # skip broken pieces
97
+ result.append((name, type_str, description))
94
98
 
95
99
  return result
96
100
 
@@ -107,20 +111,101 @@ if __name__ == "__main__":
107
111
  SAMPLE_2 = (
108
112
  "field_with_internal_quotes: Literal['type_A', "
109
113
  '"type_B_with_\'_apostrophe"] | A literal with mixed quotes,'
110
- " another_field: str"
114
+ " another_field :str| A field with a description"
115
+ )
116
+
117
+ SAMPLE_3 = (
118
+ "field_with_internal_quotes: Literal['type_A', "
119
+ '"type_B_with_\'_apostrophe"] | A literal with mixed quotes,'
120
+ " another_field | A field with a description"
121
+ )
122
+
123
+ SAMPLE_4 = "input, query, output"
124
+
125
+ SAMPLE_5 = (
126
+ "name: str | The character's full name,"
127
+ "race: str | The character's fantasy race,"
128
+ "class: Literal['mage','thief'] | The character's profession, which can be either mage or thief,"
129
+ "background: str | A brief backstory for the character"
130
+ )
131
+
132
+ SAMPLE_6 = (
133
+ "summary: str | A short blurb, e.g. key:value pairs that appear in logs"
111
134
  )
135
+ # ➜ [('summary', 'str',
136
+ # 'A short blurb, e.g. key:value pairs that appear in logs')]
112
137
 
113
- print(f"Sample 1: {SAMPLE_1}")
114
- print(f"Sample 2: {SAMPLE_2}")
138
+ SAMPLE_7 = "path: str | The literal string 'C:\\\\Program Files\\\\My,App'"
115
139
 
116
- print("\nSplitting Sample 1:")
117
- split_sample_1 = split_top_level(SAMPLE_1)
118
- print(split_sample_1)
119
- for field in split_sample_1:
120
- print(parse_schema(field))
140
+ # ➜ [('path', 'str',
141
+ # "The literal string 'C:\\Program Files\\My,App'")]
121
142
 
122
- print("\nSplitting Sample 2:")
123
- split_sample_2 = split_top_level(SAMPLE_2)
124
- print(split_sample_2)
125
- for field in split_sample_2:
126
- print(parse_schema(field))
143
+ SAMPLE_8 = (
144
+ "transform: Callable[[int, str], bool] | Function that returns True on success,"
145
+ "retries: int | How many times to retry"
146
+ )
147
+ # ➜ ('transform', 'Callable[[int, str], bool]', 'Function that returns True on success')
148
+ # ('retries', 'int', 'How many times to retry')
149
+
150
+ SAMPLE_9 = (
151
+ r"regex: str | Pattern such as r'^[A-Z\|a-z]+$',"
152
+ "flags: int | re flags to use"
153
+ )
154
+ # ➜ ('regex', 'str', "Pattern such as r'^[A-Z\\|a-z]+$'")
155
+ # ('flags', 'int', 're flags to use')
156
+
157
+ SAMPLE_10 = "id:int, name:str," # note the final comma!
158
+ # ➜ ('id', 'int', '')
159
+ # ('name', 'str', '')
160
+
161
+ SAMPLE_11 = "id:int | Primary key\nname:str | Display name\nactive:bool"
162
+ # ➜ should not work!
163
+
164
+ SAMPLE_12 = (
165
+ 'comment:str | The text "done | failed" goes here,'
166
+ 'status:Literal["done","failed"]'
167
+ )
168
+ # ➜ ('comment', 'str', 'The text "done | failed" goes here')
169
+ # ('status', 'Literal["done","failed"]', '')
170
+
171
+ SAMPLE_13 = "choice: Literal['He said \\'yes\\'', 'no'] | User response"
172
+ # ➜ ('choice', "Literal['He said \\'yes\\'', 'no']", 'User response')
173
+
174
+ SAMPLE_14 = ""
175
+ # ➜ []
176
+
177
+ SAMPLE_15 = "username"
178
+ # ➜ [('username', 'str', '')]
179
+
180
+ SAMPLE_16 = (
181
+ "payload: dict[str, list[dict[str, tuple[int,str]]]] "
182
+ "| Arbitrarily complex structure"
183
+ )
184
+ # ➜ ('payload', 'dict[str, list[dict[str, tuple[int,str]]]]',
185
+ # 'Arbitrarily complex structure')
186
+
187
+ SAMPLE_17 = "münze: str | Deutsche Münzbezeichnung, engl. 'coin'"
188
+ # ➜ [('münze', 'str', "Deutsche Münzbezeichnung, engl. 'coin'")]
189
+
190
+ for title, spec in [
191
+ ("Sample-1", SAMPLE_1),
192
+ ("Sample-2", SAMPLE_2),
193
+ ("Sample-3", SAMPLE_3),
194
+ ("Sample-4", SAMPLE_4),
195
+ ("Sample-5", SAMPLE_5),
196
+ ("Sample-6", SAMPLE_6),
197
+ ("Sample-7", SAMPLE_7),
198
+ ("Sample-8", SAMPLE_8),
199
+ ("Sample-9", SAMPLE_9),
200
+ ("Sample-10", SAMPLE_10),
201
+ ("Sample-11", SAMPLE_11),
202
+ ("Sample-12", SAMPLE_12),
203
+ ("Sample-13", SAMPLE_13),
204
+ ("Sample-14", SAMPLE_14),
205
+ ("Sample-15", SAMPLE_15),
206
+ ("Sample-16", SAMPLE_16),
207
+ ("Sample-17", SAMPLE_17),
208
+ ]:
209
+ print(f"\n{title}")
210
+ for row in parse_schema(spec):
211
+ print(row)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flock-core
3
- Version: 0.4.0b32
3
+ Version: 0.4.0b34
4
4
  Summary: Declarative LLM Orchestration at Scale
5
5
  Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
6
6
  License-File: LICENSE
@@ -20,10 +20,10 @@ flock/cli/yaml_editor.py,sha256=K3N0bh61G1TSDAZDnurqW9e_-hO6CtSQKXQqlDhCjVo,1252
20
20
  flock/cli/assets/release_notes.md,sha256=bqnk50jxM3w5uY44Dc7MkdT8XmRREFxrVBAG9XCOSSU,4896
21
21
  flock/core/__init__.py,sha256=p7lmQULRu9ejIAELfanZiyMhW0CougIPvyFHW2nqBFQ,847
22
22
  flock/core/flock.py,sha256=_T_KAFePtEGDzfiFGV1HCdz7VHzj_U0cCquhAQ4xMAM,28199
23
- flock/core/flock_agent.py,sha256=VXNsnsiqsMvrVsTZBNPCa7qCH3LdzvGKg3-mKO-Hj64,39227
23
+ flock/core/flock_agent.py,sha256=JTqaGD_OnZSd3bVU989WMsK1rAT6UGn-JYrPxFV15EE,39576
24
24
  flock/core/flock_evaluator.py,sha256=dOXZeDOGZcAmJ9ahqq_2bdGUU1VOXY4skmwTVpAjiVw,1685
25
25
  flock/core/flock_factory.py,sha256=_4zsjkEmJnCR7IvJ3SUHnDbX6c7Tt3E4P5ohxwKvE6w,3173
26
- flock/core/flock_module.py,sha256=2MdAh-n0o4uw7jogvW9iHjVPUawUNN1oGor5ego3RLI,3057
26
+ flock/core/flock_module.py,sha256=UCK6TFe4viXs596zeng0GD3gln4ZNGu_gCWkXIIMREg,3090
27
27
  flock/core/flock_registry.py,sha256=Qcu9juUFNyDAOEsqVxauwVlWdfgKZrSzc8yT8JMiK-c,24246
28
28
  flock/core/flock_router.py,sha256=1OAXDsdaIIFApEfo6SRfFEDoTuGt3Si7n2MXiySEfis,2644
29
29
  flock/core/api/__init__.py,sha256=OKlhzDWZJfA6ddBwxQUmATY0TSzESsH032u00iVGvdA,228
@@ -71,12 +71,12 @@ flock/core/tools/llm_tools.py,sha256=Bdt4Dpur5dGpxd2KFEQyxjfZazvW1HCDKY6ydMj6UgQ
71
71
  flock/core/tools/markdown_tools.py,sha256=W6fGM48yGHbifVlaOk1jOtVcybfRbRmf20VbDOZv8S4,6031
72
72
  flock/core/tools/zendesk_tools.py,sha256=deZAyUi9j-_yZaTayLQVJaFXIqIct-P6C8IGN5UU_tM,3528
73
73
  flock/core/tools/dev_tools/github.py,sha256=a2OTPXS7kWOVA4zrZHynQDcsmEi4Pac5MfSjQOLePzA,5308
74
- flock/core/util/cli_helper.py,sha256=Q8GFizNKiMW3wMJwIOobR6ldP40wYCdcNqTgD5z4sPo,49951
74
+ flock/core/util/cli_helper.py,sha256=g4u8kCtbwHRXETzBz1cw52_8muSWbWlr00CqST525jI,49963
75
75
  flock/core/util/file_path_utils.py,sha256=Odf7uU32C-x1KNighbNERSiMtkzW4h8laABIoFK7A5M,6246
76
76
  flock/core/util/hydrator.py,sha256=QJvCA8F4nkSP5akp3yg0cT6oaajOr1n7sldW5dCs6Lo,10733
77
77
  flock/core/util/input_resolver.py,sha256=KPoPSpklyCoiR2t5r6J6GJHegmPLFZ0YE126VcKBewM,4703
78
78
  flock/core/util/loader.py,sha256=j3q2qem5bFMP2SmMuYjb-ISxsNGNZd1baQmpvAnRUUk,2244
79
- flock/core/util/spliter.py,sha256=qGGbz2UN6v0nATymDzD_G656mWS9dXHrfQoore-lw9M,4027
79
+ flock/core/util/spliter.py,sha256=D-P4u3vdKQFyC4DltpCoX4PExXuOKnE-G5kBG_6H678,6770
80
80
  flock/evaluators/declarative/declarative_evaluator.py,sha256=q3qKHuKrj17mhaoOZuKh2HyVfiDBEEUk1Y1ZrejvggA,6328
81
81
  flock/evaluators/memory/memory_evaluator.py,sha256=ySwz7kcc8suXMJ7gKNSWThW8iOMlE8lUcUzEAHvv8rw,3559
82
82
  flock/evaluators/test/test_case_evaluator.py,sha256=3Emcoty0LOLLBIuPGxSpKphuZC9Fu1DTr1vbGg-hd0Q,1233
@@ -445,8 +445,8 @@ flock/workflow/agent_execution_activity.py,sha256=Gy6FtuVAjf0NiUXmC3syS2eJpNQF4R
445
445
  flock/workflow/flock_workflow.py,sha256=iSUF_soFvWar0ffpkzE4irkDZRx0p4HnwmEBi_Ne2sY,9666
446
446
  flock/workflow/temporal_config.py,sha256=3_8O7SDEjMsSMXsWJBfnb6XTp0TFaz39uyzSlMTSF_I,3988
447
447
  flock/workflow/temporal_setup.py,sha256=YIHnSBntzOchHfMSh8hoLeNXrz3B1UbR14YrR6soM7A,1606
448
- flock_core-0.4.0b32.dist-info/METADATA,sha256=XEuCjPQqyIbUkZHfPKCt-wRNAbArW9Yxj-3RxXIvwNM,17125
449
- flock_core-0.4.0b32.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
450
- flock_core-0.4.0b32.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
451
- flock_core-0.4.0b32.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
452
- flock_core-0.4.0b32.dist-info/RECORD,,
448
+ flock_core-0.4.0b34.dist-info/METADATA,sha256=xImOi53HupDqDi8RpnOjZfkS_rbLJQ6j_CrwoIR7RPA,17125
449
+ flock_core-0.4.0b34.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
450
+ flock_core-0.4.0b34.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
451
+ flock_core-0.4.0b34.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
452
+ flock_core-0.4.0b34.dist-info/RECORD,,