flock-core 0.1.1__py3-none-any.whl → 0.2.1__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 +1 -4
- flock/agents/__init__.py +0 -3
- flock/agents/batch_agent.py +140 -175
- flock/agents/loop_agent.py +117 -178
- flock/agents/trigger_agent.py +113 -191
- flock/agents/user_agent.py +145 -230
- flock/core/__init__.py +1 -7
- flock/core/context/context.py +211 -0
- flock/core/context/context_manager.py +34 -0
- flock/core/{context_vars.py → context/context_vars.py} +8 -6
- flock/core/execution/local_executor.py +27 -0
- flock/core/execution/temporal_executor.py +40 -0
- flock/core/flock.py +199 -208
- flock/core/flock_agent.py +492 -0
- flock/core/logging/__init__.py +2 -18
- flock/core/logging/formatters/base_formatter.py +36 -0
- flock/core/logging/formatters/formatter_factory.py +38 -0
- flock/core/logging/formatters/pprint_formatter.py +18 -0
- flock/core/logging/formatters/rich_formatters.py +132 -0
- flock/core/logging/formatters/theme_builder.py +480 -0
- flock/core/logging/formatters/themed_formatter.py +442 -0
- flock/core/logging/logging.py +123 -0
- flock/core/mixin/dspy_integration.py +171 -0
- flock/core/mixin/prompt_parser.py +125 -0
- flock/core/registry/agent_registry.py +103 -0
- flock/core/tools/basic_tools.py +273 -98
- flock/core/tools/dev_tools/github.py +161 -0
- flock/core/util/cli_helper.py +21 -0
- flock/core/util/input_resolver.py +156 -0
- flock/core/util/serializable.py +93 -0
- flock/themes/3024-day.toml +39 -0
- flock/themes/3024-night.toml +77 -0
- flock/themes/aardvark-blue.toml +77 -0
- flock/themes/abernathy.toml +77 -0
- flock/themes/adventure.toml +77 -0
- flock/themes/adventuretime.toml +77 -0
- flock/themes/afterglow.toml +77 -0
- flock/themes/alabaster.toml +77 -0
- flock/themes/alienblood.toml +77 -0
- flock/themes/andromeda.toml +77 -0
- flock/themes/apple-classic.toml +77 -0
- flock/themes/apple-system-colors.toml +77 -0
- flock/themes/arcoiris.toml +77 -0
- flock/themes/argonaut copy.toml +77 -0
- flock/themes/argonaut.toml +39 -0
- flock/themes/arthur.toml +77 -0
- flock/themes/ateliersulphurpool.toml +77 -0
- flock/themes/atom.toml +38 -0
- flock/themes/atom_test.toml +65 -0
- flock/themes/atomonelight.toml +77 -0
- flock/themes/aurora.toml +77 -0
- flock/themes/ayu copy.toml +77 -0
- flock/themes/ayu-light.toml +77 -0
- flock/themes/ayu-mirage.toml +77 -0
- flock/themes/ayu.toml +39 -0
- flock/themes/banana-blueberry.toml +77 -0
- flock/themes/batman.toml +77 -0
- flock/themes/belafonte-day.toml +77 -0
- flock/themes/belafonte-night.toml +77 -0
- flock/themes/birdsofparadise.toml +77 -0
- flock/themes/blazer.toml +77 -0
- flock/themes/blue-matrix.toml +77 -0
- flock/themes/blueberrypie.toml +77 -0
- flock/themes/bluedolphin.toml +77 -0
- flock/themes/blulocodark.toml +77 -0
- flock/themes/blulocolight.toml +77 -0
- flock/themes/borland.toml +77 -0
- flock/themes/breeze.toml +77 -0
- flock/themes/bright-lights.toml +77 -0
- flock/themes/broadcast.toml +77 -0
- flock/themes/brogrammer.toml +77 -0
- flock/themes/builtin-dark.toml +77 -0
- flock/themes/builtin-light.toml +77 -0
- flock/themes/builtin-pastel-dark.toml +77 -0
- flock/themes/builtin-solarized-dark.toml +77 -0
- flock/themes/builtin-solarized-light.toml +77 -0
- flock/themes/builtin-tango-dark.toml +77 -0
- flock/themes/builtin-tango-light.toml +77 -0
- flock/themes/c64.toml +77 -0
- flock/themes/calamity.toml +77 -0
- flock/themes/catppuccin-frappe.toml +77 -0
- flock/themes/catppuccin-latte.toml +77 -0
- flock/themes/catppuccin-macchiato.toml +77 -0
- flock/themes/catppuccin-mocha.toml +77 -0
- flock/themes/cga.toml +77 -0
- flock/themes/chalk.toml +77 -0
- flock/themes/chalkboard.toml +77 -0
- flock/themes/challengerdeep.toml +77 -0
- flock/themes/chester.toml +77 -0
- flock/themes/ciapre.toml +77 -0
- flock/themes/clrs.toml +77 -0
- flock/themes/cobalt-neon.toml +77 -0
- flock/themes/cobalt2.toml +77 -0
- flock/themes/coffee-theme.toml +77 -0
- flock/themes/crayonponyfish.toml +77 -0
- flock/themes/cutiepro.toml +77 -0
- flock/themes/cyberdyne.toml +77 -0
- flock/themes/cyberpunk.toml +77 -0
- flock/themes/cyberpunkscarletprotocol.toml +77 -0
- flock/themes/dark+.toml +77 -0
- flock/themes/dark-pastel.toml +77 -0
- flock/themes/darkermatrix.toml +77 -0
- flock/themes/darkmatrix.toml +77 -0
- flock/themes/darkside.toml +77 -0
- flock/themes/dayfox.toml +77 -0
- flock/themes/deep.toml +77 -0
- flock/themes/desert.toml +77 -0
- flock/themes/dimidium.toml +77 -0
- flock/themes/dimmedmonokai.toml +77 -0
- flock/themes/django.toml +77 -0
- flock/themes/djangorebornagain.toml +77 -0
- flock/themes/djangosmooth.toml +77 -0
- flock/themes/doom-peacock.toml +77 -0
- flock/themes/doomone.toml +77 -0
- flock/themes/dotgov.toml +77 -0
- flock/themes/dracula+.toml +77 -0
- flock/themes/dracula.toml +77 -0
- flock/themes/duckbones.toml +77 -0
- flock/themes/duotone-dark.toml +77 -0
- flock/themes/earthsong.toml +77 -0
- flock/themes/elemental.toml +77 -0
- flock/themes/elementary.toml +77 -0
- flock/themes/encom.toml +77 -0
- flock/themes/espresso-libre.toml +77 -0
- flock/themes/espresso.toml +77 -0
- flock/themes/everblush.toml +77 -0
- flock/themes/fahrenheit.toml +77 -0
- flock/themes/fairyfloss.toml +77 -0
- flock/themes/farmhouse-dark.toml +77 -0
- flock/themes/farmhouse-light.toml +77 -0
- flock/themes/fideloper.toml +77 -0
- flock/themes/firefly-traditional.toml +77 -0
- flock/themes/firefoxdev.toml +77 -0
- flock/themes/firewatch.toml +77 -0
- flock/themes/fishtank.toml +77 -0
- flock/themes/flat.toml +77 -0
- flock/themes/flatland.toml +77 -0
- flock/themes/flexoki-dark.toml +77 -0
- flock/themes/flexoki-light.toml +77 -0
- flock/themes/floraverse.toml +77 -0
- flock/themes/forestblue.toml +77 -0
- flock/themes/framer.toml +77 -0
- flock/themes/frontenddelight.toml +77 -0
- flock/themes/funforrest.toml +77 -0
- flock/themes/galaxy.toml +77 -0
- flock/themes/galizur.toml +77 -0
- flock/themes/github-dark.toml +77 -0
- flock/themes/github.toml +77 -0
- flock/themes/glacier.toml +77 -0
- flock/themes/grape.toml +77 -0
- flock/themes/grass.toml +77 -0
- flock/themes/grey-green.toml +77 -0
- flock/themes/gruber-darker.toml +77 -0
- flock/themes/gruvboxdark.toml +77 -0
- flock/themes/gruvboxdarkhard.toml +77 -0
- flock/themes/gruvboxlight.toml +77 -0
- flock/themes/guezwhoz.toml +77 -0
- flock/themes/hacktober.toml +77 -0
- flock/themes/hardcore.toml +77 -0
- flock/themes/harper.toml +77 -0
- flock/themes/hax0r-blue.toml +77 -0
- flock/themes/hax0r-gr33n.toml +77 -0
- flock/themes/hax0r-r3d.toml +77 -0
- flock/themes/highway.toml +77 -0
- flock/themes/hipster-green.toml +77 -0
- flock/themes/hivacruz.toml +77 -0
- flock/themes/homebrew.toml +77 -0
- flock/themes/hopscotch.256.toml +77 -0
- flock/themes/hopscotch.toml +77 -0
- flock/themes/hurtado.toml +77 -0
- flock/themes/hybrid.toml +77 -0
- flock/themes/ic-green-ppl.toml +77 -0
- flock/themes/ic-orange-ppl.toml +77 -0
- flock/themes/iceberg-dark.toml +77 -0
- flock/themes/iceberg-light.toml +77 -0
- flock/themes/idea.toml +77 -0
- flock/themes/idletoes.toml +77 -0
- flock/themes/ir-black.toml +77 -0
- flock/themes/iterm2-dark-background.toml +77 -0
- flock/themes/iterm2-default.toml +77 -0
- flock/themes/iterm2-light-background.toml +77 -0
- flock/themes/iterm2-pastel-dark-background.toml +77 -0
- flock/themes/iterm2-smoooooth.toml +77 -0
- flock/themes/iterm2-solarized-dark.toml +77 -0
- flock/themes/iterm2-solarized-light.toml +77 -0
- flock/themes/iterm2-tango-dark.toml +77 -0
- flock/themes/iterm2-tango-light.toml +77 -0
- flock/themes/jackie-brown.toml +77 -0
- flock/themes/japanesque.toml +77 -0
- flock/themes/jellybeans.toml +77 -0
- flock/themes/jetbrains-darcula.toml +77 -0
- flock/themes/jubi.toml +77 -0
- flock/themes/kanagawabones.toml +77 -0
- flock/themes/kibble.toml +77 -0
- flock/themes/kolorit.toml +77 -0
- flock/themes/konsolas.toml +77 -0
- flock/themes/kurokula.toml +77 -0
- flock/themes/lab-fox.toml +77 -0
- flock/themes/laser.toml +77 -0
- flock/themes/later-this-evening.toml +77 -0
- flock/themes/lavandula.toml +77 -0
- flock/themes/liquidcarbon.toml +77 -0
- flock/themes/liquidcarbontransparent.toml +77 -0
- flock/themes/liquidcarbontransparentinverse.toml +77 -0
- flock/themes/lovelace.toml +77 -0
- flock/themes/man-page.toml +77 -0
- flock/themes/mariana.toml +77 -0
- flock/themes/material.toml +77 -0
- flock/themes/materialdark.toml +77 -0
- flock/themes/materialdarker.toml +77 -0
- flock/themes/materialdesigncolors.toml +77 -0
- flock/themes/materialocean.toml +77 -0
- flock/themes/mathias.toml +77 -0
- flock/themes/matrix.toml +77 -0
- flock/themes/medallion.toml +77 -0
- flock/themes/mellifluous.toml +77 -0
- flock/themes/midnight-in-mojave.toml +77 -0
- flock/themes/mirage.toml +77 -0
- flock/themes/misterioso.toml +77 -0
- flock/themes/molokai.toml +77 -0
- flock/themes/monalisa.toml +77 -0
- flock/themes/monokai-remastered.toml +77 -0
- flock/themes/monokai-soda.toml +77 -0
- flock/themes/monokai-vivid.toml +77 -0
- flock/themes/n0tch2k.toml +77 -0
- flock/themes/neobones-dark.toml +77 -0
- flock/themes/neobones-light.toml +77 -0
- flock/themes/neon.toml +77 -0
- flock/themes/neopolitan.toml +77 -0
- flock/themes/neutron.toml +77 -0
- flock/themes/night-owlish-light.toml +77 -0
- flock/themes/nightfox.toml +77 -0
- flock/themes/nightlion-v1.toml +77 -0
- flock/themes/nightlion-v2.toml +77 -0
- flock/themes/niji.toml +77 -0
- flock/themes/nocturnal-winter.toml +77 -0
- flock/themes/nord-light.toml +77 -0
- flock/themes/nord.toml +77 -0
- flock/themes/novel.toml +77 -0
- flock/themes/nvimdark.toml +77 -0
- flock/themes/nvimlight.toml +77 -0
- flock/themes/obsidian.toml +77 -0
- flock/themes/ocean.toml +77 -0
- flock/themes/oceanic-next.toml +77 -0
- flock/themes/oceanicmaterial.toml +77 -0
- flock/themes/ollie.toml +77 -0
- flock/themes/onehalfdark.toml +77 -0
- flock/themes/onehalflight.toml +77 -0
- flock/themes/operator-mono-dark.toml +77 -0
- flock/themes/overnight-slumber.toml +77 -0
- flock/themes/oxocarbon.toml +77 -0
- flock/themes/palenighthc.toml +77 -0
- flock/themes/pandora.toml +77 -0
- flock/themes/paraiso-dark.toml +77 -0
- flock/themes/paulmillr.toml +77 -0
- flock/themes/pencildark.toml +77 -0
- flock/themes/pencillight.toml +77 -0
- flock/themes/peppermint.toml +77 -0
- flock/themes/piatto-light.toml +77 -0
- flock/themes/pnevma.toml +77 -0
- flock/themes/popping-and-locking.toml +77 -0
- flock/themes/primary.toml +77 -0
- flock/themes/pro-light.toml +77 -0
- flock/themes/pro.toml +77 -0
- flock/themes/purple-rain.toml +77 -0
- flock/themes/purplepeter.toml +77 -0
- flock/themes/rapture.toml +77 -0
- flock/themes/raycast-dark.toml +77 -0
- flock/themes/raycast-light.toml +77 -0
- flock/themes/rebecca.toml +77 -0
- flock/themes/red-alert.toml +77 -0
- flock/themes/red-planet.toml +77 -0
- flock/themes/red-sands.toml +77 -0
- flock/themes/relaxed.toml +77 -0
- flock/themes/retro.toml +77 -0
- flock/themes/rippedcasts.toml +77 -0
- flock/themes/rose-pine-dawn.toml +77 -0
- flock/themes/rose-pine-moon.toml +77 -0
- flock/themes/rose-pine.toml +77 -0
- flock/themes/rouge-2.toml +77 -0
- flock/themes/royal.toml +77 -0
- flock/themes/ryuuko.toml +77 -0
- flock/themes/sakura.toml +77 -0
- flock/themes/scarlet-protocol.toml +77 -0
- flock/themes/seafoam-pastel.toml +77 -0
- flock/themes/seashells.toml +77 -0
- flock/themes/seoulbones-dark.toml +77 -0
- flock/themes/seoulbones-light.toml +77 -0
- flock/themes/seti.toml +77 -0
- flock/themes/shades-of-purple.toml +77 -0
- flock/themes/shaman.toml +77 -0
- flock/themes/slate.toml +77 -0
- flock/themes/sleepyhollow.toml +77 -0
- flock/themes/smyck.toml +77 -0
- flock/themes/snazzy.toml +77 -0
- flock/themes/softserver.toml +77 -0
- flock/themes/solarized-darcula.toml +77 -0
- flock/themes/solarized-dark---patched.toml +77 -0
- flock/themes/solarized-dark-higher-contrast.toml +77 -0
- flock/themes/spacedust.toml +77 -0
- flock/themes/spacegray-eighties-dull.toml +77 -0
- flock/themes/spacegray-eighties.toml +77 -0
- flock/themes/spacegray.toml +77 -0
- flock/themes/spiderman.toml +77 -0
- flock/themes/spring.toml +77 -0
- flock/themes/square.toml +77 -0
- flock/themes/sublette.toml +77 -0
- flock/themes/subliminal.toml +77 -0
- flock/themes/sugarplum.toml +77 -0
- flock/themes/sundried.toml +77 -0
- flock/themes/symfonic.toml +77 -0
- flock/themes/synthwave-everything.toml +77 -0
- flock/themes/synthwave.toml +77 -0
- flock/themes/synthwavealpha.toml +77 -0
- flock/themes/tango-adapted.toml +77 -0
- flock/themes/tango-half-adapted.toml +77 -0
- flock/themes/teerb.toml +77 -0
- flock/themes/terafox.toml +77 -0
- flock/themes/terminal-basic.toml +77 -0
- flock/themes/thayer-bright.toml +77 -0
- flock/themes/the-hulk.toml +77 -0
- flock/themes/tinacious-design-(dark).toml +77 -0
- flock/themes/tinacious-design-(light).toml +77 -0
- flock/themes/tokyonight-day.toml +77 -0
- flock/themes/tokyonight-storm.toml +77 -0
- flock/themes/tokyonight.toml +77 -0
- flock/themes/tomorrow-night-blue.toml +77 -0
- flock/themes/tomorrow-night-bright.toml +77 -0
- flock/themes/tomorrow-night-burns.toml +77 -0
- flock/themes/tomorrow-night-eighties.toml +77 -0
- flock/themes/tomorrow-night.toml +77 -0
- flock/themes/tomorrow.toml +77 -0
- flock/themes/toychest.toml +77 -0
- flock/themes/treehouse.toml +77 -0
- flock/themes/twilight.toml +77 -0
- flock/themes/ubuntu.toml +77 -0
- flock/themes/ultradark.toml +77 -0
- flock/themes/ultraviolent.toml +77 -0
- flock/themes/underthesea.toml +77 -0
- flock/themes/unikitty.toml +77 -0
- flock/themes/urple.toml +77 -0
- flock/themes/vaughn.toml +77 -0
- flock/themes/vesper.toml +77 -0
- flock/themes/vibrantink.toml +77 -0
- flock/themes/vimbones.toml +77 -0
- flock/themes/violet-dark.toml +77 -0
- flock/themes/violet-light.toml +77 -0
- flock/themes/warmneon.toml +77 -0
- flock/themes/wez.toml +77 -0
- flock/themes/whimsy.toml +77 -0
- flock/themes/wildcherry.toml +77 -0
- flock/themes/wilmersdorf.toml +77 -0
- flock/themes/wombat.toml +77 -0
- flock/themes/wryan.toml +77 -0
- flock/themes/xcodedark.toml +77 -0
- flock/themes/xcodedarkhc.toml +77 -0
- flock/themes/xcodelight.toml +77 -0
- flock/themes/xcodelighthc.toml +77 -0
- flock/themes/xcodewwdc.toml +77 -0
- flock/themes/zenbones-dark.toml +77 -0
- flock/themes/zenbones-light.toml +77 -0
- flock/themes/zenbones.toml +77 -0
- flock/themes/zenburn.toml +77 -0
- flock/themes/zenburned.toml +77 -0
- flock/themes/zenwritten-dark.toml +77 -0
- flock/themes/zenwritten-light.toml +77 -0
- flock/workflow/activities.py +117 -115
- flock/workflow/agent_activities.py +24 -26
- flock/workflow/temporal_setup.py +38 -37
- flock/workflow/workflow.py +58 -53
- flock_core-0.2.1.dist-info/METADATA +287 -0
- flock_core-0.2.1.dist-info/RECORD +375 -0
- {flock_core-0.1.1.dist-info → flock_core-0.2.1.dist-info}/licenses/LICENSE +21 -21
- flock/agents/declarative_agent.py +0 -166
- flock/app/components/__init__.py +0 -14
- flock/app/components/charts/agent_workflow.py +0 -14
- flock/app/components/charts/core_architecture.py +0 -14
- flock/app/components/charts/tool_system.py +0 -14
- flock/app/components/history_grid.py +0 -168
- flock/app/components/history_grid_alt.py +0 -189
- flock/app/components/sidebar.py +0 -19
- flock/app/components/theme.py +0 -9
- flock/app/components/util.py +0 -18
- flock/app/hive_app.py +0 -118
- flock/app/html/d3.html +0 -179
- flock/app/modules/__init__.py +0 -12
- flock/app/modules/about.py +0 -17
- flock/app/modules/agent_detail.py +0 -70
- flock/app/modules/agent_list.py +0 -59
- flock/app/modules/playground.py +0 -322
- flock/app/modules/settings.py +0 -96
- flock/core/agent.py +0 -150
- flock/core/agent_registry.py +0 -162
- flock/core/context.py +0 -279
- flock/core/handoff/handoff_base.py +0 -12
- flock/core/logging/error_handler.py +0 -84
- flock/core/logging/formatters.py +0 -122
- flock/core/logging/handlers.py +0 -117
- flock/core/logging/logger.py +0 -107
- flock/core/serializable.py +0 -206
- flock_core-0.1.1.dist-info/METADATA +0 -449
- flock_core-0.1.1.dist-info/RECORD +0 -48
- flock_core-0.1.1.dist-info/entry_points.txt +0 -2
- /flock/{core/config/declarative_agent_config.py → workflow/__init__.py} +0 -0
- {flock_core-0.1.1.dist-info → flock_core-0.2.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
"""FlockAgent is the core, declarative base class for all agents in the Flock framework."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any, Literal, Union
|
|
7
|
+
|
|
8
|
+
import cloudpickle
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
from flock.core.context.context import FlockContext
|
|
12
|
+
from flock.core.logging.logging import get_logger
|
|
13
|
+
from flock.core.mixin.dspy_integration import DSPyIntegrationMixin
|
|
14
|
+
from flock.core.mixin.prompt_parser import PromptParserMixin
|
|
15
|
+
|
|
16
|
+
logger = get_logger("flock")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class FlockAgentConfig:
|
|
21
|
+
"""Configuration options for a FlockAgent."""
|
|
22
|
+
|
|
23
|
+
save_to_file: bool = field(
|
|
24
|
+
default=False,
|
|
25
|
+
metadata={
|
|
26
|
+
"description": "Saves the serialized agent to a file every time it gets serialized."
|
|
27
|
+
},
|
|
28
|
+
)
|
|
29
|
+
data_type: Literal["json", "cloudpickle", "msgpack"] = "cloudpickle"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class HandoffBase:
|
|
34
|
+
"""Base class for handoff returns."""
|
|
35
|
+
|
|
36
|
+
next_agent: Union[str, "FlockAgent"] = field(
|
|
37
|
+
default="", metadata={"description": "Next agent to invoke"}
|
|
38
|
+
)
|
|
39
|
+
input: dict[str, Any] = field(
|
|
40
|
+
default_factory=dict,
|
|
41
|
+
metadata={"description": "Input data for the next agent"},
|
|
42
|
+
)
|
|
43
|
+
context: FlockContext = field(
|
|
44
|
+
default=None, metadata={"description": "Override context parameters"}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
49
|
+
"""FlockAgent is the core, declarative base class for all agents in the Flock framework.
|
|
50
|
+
|
|
51
|
+
Due to its declarative nature, FlockAgent does not rely on constructing classical prompts manually.
|
|
52
|
+
Instead, each agent is defined by simply declaring:
|
|
53
|
+
- Its expected inputs (what data it needs),
|
|
54
|
+
- Its expected outputs (what data it produces), and
|
|
55
|
+
- Any optional tools it can use during execution.
|
|
56
|
+
|
|
57
|
+
In a declarative model, you describe *what* you expect rather than *how* to compute it. This means that
|
|
58
|
+
instead of embedding prompt engineering logic directly in your code, you specify a concise signature.
|
|
59
|
+
At runtime, the Flock framework automatically:
|
|
60
|
+
- Resolves the inputs from a pre-computed context,
|
|
61
|
+
- Constructs a precise prompt for the underlying language model (using metadata such as type hints
|
|
62
|
+
and human-readable descriptions), and
|
|
63
|
+
- Invokes the appropriate tools if needed.
|
|
64
|
+
|
|
65
|
+
This approach minimizes hidden dependencies and boilerplate code. It allows developers to focus solely on
|
|
66
|
+
what data the agent should work with and what result it should produce, without worrying about the intricacies
|
|
67
|
+
of prompt formatting or context management.
|
|
68
|
+
|
|
69
|
+
For details on how Flock resolves inputs, please refer to the documentation.
|
|
70
|
+
|
|
71
|
+
Key benefits of the declarative approach include:
|
|
72
|
+
- **Clarity:** The agent's interface (inputs and outputs) is explicitly defined.
|
|
73
|
+
- **Reusability:** Agents can be easily serialized, shared, and reused since they are built as Pydantic models.
|
|
74
|
+
- **Modularity:** By receiving a dictionary of pre-resolved inputs, agents operate independently of the global context,
|
|
75
|
+
making them easier to test and debug.
|
|
76
|
+
- **Extensibility:** Additional metadata (like detailed descriptions for each key) can be embedded and later used to
|
|
77
|
+
refine the prompt for the LLM.
|
|
78
|
+
|
|
79
|
+
Since FlockAgent is a Pydantic BaseModel, it can be serialized to and from JSON. This ensures that the agent's
|
|
80
|
+
configuration and state are easily stored, transmitted, and reproduced.
|
|
81
|
+
|
|
82
|
+
**Implementation Example:**
|
|
83
|
+
|
|
84
|
+
Below is an example of how to define and instantiate a FlockAgent:
|
|
85
|
+
|
|
86
|
+
from flock_agent import FlockAgent
|
|
87
|
+
import basic_tools
|
|
88
|
+
|
|
89
|
+
# Define an agent by declaring its inputs, outputs, and optional tools.
|
|
90
|
+
idea_agent = FlockAgent(
|
|
91
|
+
name="idea_agent",
|
|
92
|
+
input="query: str | The search query, context: dict | The full conversation context",
|
|
93
|
+
output="a_fun_software_project_idea: str | The generated software project idea",
|
|
94
|
+
tools=[basic_tools.web_search_tavily],
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# At runtime, Flock automatically resolves inputs and calls the agent:
|
|
98
|
+
resolved_inputs = {
|
|
99
|
+
"query": "A new social media app",
|
|
100
|
+
"context": {"previous_idea": "a messaging platform"}
|
|
101
|
+
}
|
|
102
|
+
result = await idea_agent.run(resolved_inputs)
|
|
103
|
+
|
|
104
|
+
In this example:
|
|
105
|
+
- The agent declares that it needs a `query` (a string describing what to search for) and a `context` (a dictionary
|
|
106
|
+
containing additional information).
|
|
107
|
+
- It produces a `a_fun_software_project_idea` (a string with the generated idea).
|
|
108
|
+
- The tool `basic_tools.web_search_tavily` is available for the agent to use if needed.
|
|
109
|
+
- When the agent is run, the Flock framework resolves the inputs, constructs the appropriate prompt using the
|
|
110
|
+
declared metadata, and returns the result.
|
|
111
|
+
|
|
112
|
+
This declarative style streamlines agent creation and execution, making the framework highly modular, testable,
|
|
113
|
+
and production-ready.
|
|
114
|
+
|
|
115
|
+
In the future options will be provided to optimize the "hidden" prompt generation and execution so the agent will
|
|
116
|
+
perform close to its theoretical maximum.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
name: str = Field(..., description="Unique identifier for the agent.")
|
|
120
|
+
model: str = Field(
|
|
121
|
+
"openai/gpt-4o", description="The model to use (e.g., 'openai/gpt-4o')."
|
|
122
|
+
)
|
|
123
|
+
description: str | Callable[..., str] = Field(
|
|
124
|
+
"", description="A human-readable description of the agent."
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
input: str | None = Field(
|
|
128
|
+
None,
|
|
129
|
+
description=(
|
|
130
|
+
"A comma-separated list of input keys. Optionally supports type hints (:) and descriptions (|). "
|
|
131
|
+
"For example: 'query: str | The search query, chapter_list: list[str] | The chapter list of the document'."
|
|
132
|
+
),
|
|
133
|
+
)
|
|
134
|
+
output: str | None = Field(
|
|
135
|
+
None,
|
|
136
|
+
description=(
|
|
137
|
+
"A comma-separated list of output keys. Optionally supports type hints (:) and descriptions (|). "
|
|
138
|
+
"For example: 'result|The generated result, summary|A brief summary'."
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
tools: list[Callable[..., Any]] | None = Field(
|
|
143
|
+
default=None,
|
|
144
|
+
description="An optional list of callable tools that the agent can leverage during execution.",
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
use_cache: bool = Field(
|
|
148
|
+
default=False,
|
|
149
|
+
description="Set to True to enable caching of the agent's results.",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
hand_off: str | Callable[..., Any] | None = Field(
|
|
153
|
+
None,
|
|
154
|
+
description=(
|
|
155
|
+
"Specifies the next agent in the workflow or a callable that determines the handoff. "
|
|
156
|
+
"This allows chaining of agents."
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
termination: str | None = Field(
|
|
161
|
+
None,
|
|
162
|
+
description="An optional termination condition or phrase used to indicate when the agent should stop processing.",
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
config: FlockAgentConfig = Field(
|
|
166
|
+
default_factory=FlockAgentConfig,
|
|
167
|
+
description="Configuration options for the agent, such as serialization settings.",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Lifecycle hooks
|
|
171
|
+
async def initialize(self, inputs: dict[str, Any]) -> None:
|
|
172
|
+
"""The very first thing to get called.
|
|
173
|
+
|
|
174
|
+
Override this method to perform any setup or configuration tasks,
|
|
175
|
+
such as loading resources or validating inputs.
|
|
176
|
+
"""
|
|
177
|
+
pass
|
|
178
|
+
|
|
179
|
+
async def terminate(
|
|
180
|
+
self, inputs: dict[str, Any], result: dict[str, Any]
|
|
181
|
+
) -> None:
|
|
182
|
+
"""The very last thing to get called.
|
|
183
|
+
|
|
184
|
+
Override this method to perform any cleanup tasks,
|
|
185
|
+
such as releasing resources or logging results.
|
|
186
|
+
"""
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
async def on_error(self, error: Exception, inputs: dict[str, Any]) -> None:
|
|
190
|
+
"""Called if the agent encounters an error during execution.
|
|
191
|
+
|
|
192
|
+
Override this method to implement
|
|
193
|
+
custom error handling or recovery strategies.
|
|
194
|
+
"""
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
async def evaluate(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
198
|
+
"""Process the agent's task using the provided inputs and return the result.
|
|
199
|
+
|
|
200
|
+
This asynchronous method is the core execution engine for a FlockAgent. It performs the following steps:
|
|
201
|
+
|
|
202
|
+
1. **Extract Descriptions:**
|
|
203
|
+
Parses the agent's configured input and output strings to extract human-readable descriptions.
|
|
204
|
+
These strings are expected to use the format "key: type_hint | description". The method removes the
|
|
205
|
+
type hints and builds dictionaries that map each key to its corresponding description.
|
|
206
|
+
|
|
207
|
+
2. **Construct the Prompt:**
|
|
208
|
+
Based on the extracted descriptions, the method builds a detailed prompt that clearly lists all input
|
|
209
|
+
fields (with their descriptions) and output fields. This prompt is designed to guide the language model,
|
|
210
|
+
ensuring that it understands what inputs are provided and what outputs are expected.
|
|
211
|
+
|
|
212
|
+
3. **Configure the Language Model:**
|
|
213
|
+
The method initializes and configures a language model using the dspy library. The agent's model
|
|
214
|
+
(e.g., "openai/gpt-4o") is used to set up the language model, ensuring that it is ready to process the prompt.
|
|
215
|
+
|
|
216
|
+
4. **Execute the Task:**
|
|
217
|
+
Depending on whether the agent has been configured with additional tools:
|
|
218
|
+
- **With Tools:** A ReAct task is instantiated. This task interleaves reasoning and tool usage,
|
|
219
|
+
allowing the agent to leverage external functionalities during execution.
|
|
220
|
+
- **Without Tools:** A Predict task is used for a straightforward generation based on the prompt.
|
|
221
|
+
|
|
222
|
+
5. **Process the Result:**
|
|
223
|
+
After execution, the method attempts to convert the result to a dictionary. It also ensures that each
|
|
224
|
+
expected input key is present in the output (by setting a default value from the inputs if necessary).
|
|
225
|
+
|
|
226
|
+
6. **Error Handling:**
|
|
227
|
+
Any exceptions raised during the process are caught, logged (or printed), and then re-raised to allow
|
|
228
|
+
higher-level error handling. This ensures that errors are not silently ignored and can be properly diagnosed.
|
|
229
|
+
|
|
230
|
+
**Arguments:**
|
|
231
|
+
inputs (dict[str, Any]): A dictionary containing all the resolved input values required by the agent.
|
|
232
|
+
These inputs are typically obtained from the global context or from the output of a previous agent.
|
|
233
|
+
|
|
234
|
+
**Returns:**
|
|
235
|
+
dict[str, Any]: A dictionary containing the output generated by the agent. This output adheres to the
|
|
236
|
+
agent's declared output fields and includes any fallback values for missing inputs.
|
|
237
|
+
|
|
238
|
+
**Usage Example:**
|
|
239
|
+
|
|
240
|
+
Suppose an agent is declared with the following configuration:
|
|
241
|
+
input = "query: str | The search query, context: dict | Additional context"
|
|
242
|
+
output = "idea: str | The generated software project idea"
|
|
243
|
+
|
|
244
|
+
When invoked with:
|
|
245
|
+
inputs = {"query": "build an app", "context": {"previous_idea": "messaging app"}}
|
|
246
|
+
|
|
247
|
+
The method will:
|
|
248
|
+
- Parse the descriptions to create:
|
|
249
|
+
input_descriptions = {"query": "The search query", "context": "Additional context"}
|
|
250
|
+
output_descriptions = {"idea": "The generated software project idea"}
|
|
251
|
+
- Construct a prompt that lists these inputs and outputs clearly.
|
|
252
|
+
- Configure the language model and execute the appropriate task (ReAct if tools are provided, otherwise Predict).
|
|
253
|
+
- Return a dictionary similar to:
|
|
254
|
+
{"idea": "A fun app idea based on ...", "query": "build an app", "context": {"previous_idea": "messaging app"}}
|
|
255
|
+
"""
|
|
256
|
+
try:
|
|
257
|
+
self.__dspy_signature = self.create_dspy_signature_class(
|
|
258
|
+
self.name, self.description, f"{self.input} -> {self.output}"
|
|
259
|
+
)
|
|
260
|
+
# Initialize the language model.
|
|
261
|
+
self._configure_language_model()
|
|
262
|
+
# Select the appropriate DSPy task based on tool availability.
|
|
263
|
+
agent_task = self._select_task(self.__dspy_signature)
|
|
264
|
+
# Execute the task with the provided inputs.
|
|
265
|
+
result = agent_task(**inputs)
|
|
266
|
+
# Process the result and ensure fallback values for missing keys.
|
|
267
|
+
result = self._process_result(result, inputs)
|
|
268
|
+
return result
|
|
269
|
+
except Exception as eval_error:
|
|
270
|
+
logger.error(
|
|
271
|
+
f"Error during evaluation in agent '{self.name}': {eval_error}"
|
|
272
|
+
)
|
|
273
|
+
raise
|
|
274
|
+
|
|
275
|
+
async def run(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
276
|
+
"""Run the agent with the given inputs and return its generated output.
|
|
277
|
+
|
|
278
|
+
This method represents the primary execution flow for a FlockAgent and performs the following
|
|
279
|
+
lifecycle steps in sequence:
|
|
280
|
+
|
|
281
|
+
1. **Initialization:**
|
|
282
|
+
Calls the `initialize(inputs)` hook to perform any necessary pre-run setup, such as input
|
|
283
|
+
validation, resource allocation, or logging. This ensures that the agent is properly configured
|
|
284
|
+
before processing begins.
|
|
285
|
+
|
|
286
|
+
2. **Evaluation:**
|
|
287
|
+
Invokes the internal `_evaluate(inputs)` method, which constructs a detailed prompt (incorporating
|
|
288
|
+
input and output descriptions), configures the underlying language model via dspy, and executes the
|
|
289
|
+
main task (using a ReAct task if tools are provided, or a Predict task otherwise).
|
|
290
|
+
|
|
291
|
+
3. **Termination:**
|
|
292
|
+
Calls the `terminate(inputs, result)` hook after evaluation, allowing the agent to clean up any
|
|
293
|
+
resources or perform post-run actions (such as logging the output).
|
|
294
|
+
|
|
295
|
+
4. **Output Return:**
|
|
296
|
+
Returns a dictionary containing the agent's output. This output conforms to the agent's declared
|
|
297
|
+
output fields and may include any default or fallback values for missing inputs.
|
|
298
|
+
|
|
299
|
+
If an error occurs during any of these steps, the `on_error(error, inputs)` hook is invoked to handle
|
|
300
|
+
the exception (for instance, by logging detailed error information). The error is then re-raised, ensuring
|
|
301
|
+
that higher-level error management can address the failure.
|
|
302
|
+
|
|
303
|
+
**Arguments:**
|
|
304
|
+
inputs (dict[str, Any]): A dictionary containing the resolved input values required by the agent.
|
|
305
|
+
These inputs are typically derived from the agent's declared input signature and may include data
|
|
306
|
+
provided by previous agents or from a global context.
|
|
307
|
+
|
|
308
|
+
**Returns:**
|
|
309
|
+
dict[str, Any]: A dictionary containing the output generated by the agent. The output structure
|
|
310
|
+
adheres to the agent's declared output fields.
|
|
311
|
+
|
|
312
|
+
**Example:**
|
|
313
|
+
Suppose an agent is defined with:
|
|
314
|
+
input = "query: str | The search query, context: dict | Additional context"
|
|
315
|
+
output = "result: str | The generated idea"
|
|
316
|
+
When executed with:
|
|
317
|
+
inputs = {"query": "build a chatbot", "context": {"user": "Alice"}}
|
|
318
|
+
The method might return:
|
|
319
|
+
{"result": "A conversational chatbot that uses AI to...", "query": "build a chatbot", "context": {"user": "Alice"}}
|
|
320
|
+
"""
|
|
321
|
+
try:
|
|
322
|
+
await self.initialize(inputs)
|
|
323
|
+
result = await self.evaluate(inputs)
|
|
324
|
+
await self.terminate(inputs, result)
|
|
325
|
+
return result
|
|
326
|
+
except Exception as run_error:
|
|
327
|
+
await self.on_error(run_error, inputs)
|
|
328
|
+
logger.error(f"Error running agent '{self.name}': {run_error}")
|
|
329
|
+
raise
|
|
330
|
+
|
|
331
|
+
async def run_temporal(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
332
|
+
"""Execute this agent via a Temporal workflow for enhanced fault tolerance and asynchronous processing.
|
|
333
|
+
|
|
334
|
+
This method enables remote execution of the agent within a Temporal environment, leveraging Temporal's
|
|
335
|
+
capabilities for persistence, retries, and distributed error handling. The workflow encapsulates the agent's
|
|
336
|
+
logic so that it can run on Temporal workers, providing robustness and scalability in production systems.
|
|
337
|
+
|
|
338
|
+
The method performs these steps:
|
|
339
|
+
1. **Connect to Temporal:**
|
|
340
|
+
Establishes a connection to the Temporal server using a Temporal client configured with the appropriate
|
|
341
|
+
namespace (default is "default").
|
|
342
|
+
2. **Serialization:**
|
|
343
|
+
Serializes the agent instance (via `to_dict()`) along with the provided inputs. This step converts any
|
|
344
|
+
callable objects (e.g., lifecycle hooks or tools) into a storable format using cloudpickle.
|
|
345
|
+
3. **Activity Invocation:**
|
|
346
|
+
Triggers a designated Temporal activity (e.g., `run_flock_agent_activity`) by passing the serialized agent
|
|
347
|
+
data and inputs. The Temporal activity is responsible for executing the agent's logic in a fault-tolerant
|
|
348
|
+
manner.
|
|
349
|
+
4. **Return Output:**
|
|
350
|
+
Awaits and returns the resulting output from the Temporal workflow as a dictionary, consistent with the
|
|
351
|
+
output structure of the local `run()` method.
|
|
352
|
+
|
|
353
|
+
If any error occurs during these steps, the error is logged and re-raised to ensure that failure is properly
|
|
354
|
+
handled by higher-level error management systems.
|
|
355
|
+
|
|
356
|
+
**Arguments:**
|
|
357
|
+
inputs (dict[str, Any]): A dictionary containing the resolved inputs required by the agent, similar to those
|
|
358
|
+
provided to the local `run()` method.
|
|
359
|
+
|
|
360
|
+
**Returns:**
|
|
361
|
+
dict[str, Any]: A dictionary containing the output produced by the agent after remote execution via Temporal.
|
|
362
|
+
The output format is consistent with that of the local `run()` method.
|
|
363
|
+
|
|
364
|
+
**Example:**
|
|
365
|
+
Given an agent defined with:
|
|
366
|
+
input = "query: str | The search query, context: dict | Additional context"
|
|
367
|
+
output = "result: str | The generated idea"
|
|
368
|
+
Calling:
|
|
369
|
+
result = await agent.run_temporal({"query": "analyze data", "context": {"source": "sales"}})
|
|
370
|
+
will execute the agent on a Temporal worker and return the output in a structured dictionary format.
|
|
371
|
+
"""
|
|
372
|
+
try:
|
|
373
|
+
from temporalio.client import Client
|
|
374
|
+
|
|
375
|
+
from flock.workflow.agent_activities import run_flock_agent_activity
|
|
376
|
+
from flock.workflow.temporal_setup import run_activity
|
|
377
|
+
|
|
378
|
+
client = await Client.connect("localhost:7233", namespace="default")
|
|
379
|
+
agent_data = self.to_dict()
|
|
380
|
+
inputs_data = inputs
|
|
381
|
+
|
|
382
|
+
result = await run_activity(
|
|
383
|
+
client,
|
|
384
|
+
self.name,
|
|
385
|
+
run_flock_agent_activity,
|
|
386
|
+
{"agent_data": agent_data, "inputs": inputs_data},
|
|
387
|
+
)
|
|
388
|
+
return result
|
|
389
|
+
|
|
390
|
+
except Exception as temporal_error:
|
|
391
|
+
logger.error(
|
|
392
|
+
f"Error running Temporal workflow for agent '{self.name}': {temporal_error}"
|
|
393
|
+
)
|
|
394
|
+
raise
|
|
395
|
+
|
|
396
|
+
def to_dict(self) -> dict[str, Any]:
|
|
397
|
+
"""Serialize the FlockAgent instance to a dictionary.
|
|
398
|
+
|
|
399
|
+
This method converts the entire agent instance—including its configuration, state, and lifecycle hooks—
|
|
400
|
+
into a dictionary format. It uses cloudpickle to serialize any callable objects (such as functions or
|
|
401
|
+
methods), converting them into hexadecimal string representations. This ensures that the agent can be
|
|
402
|
+
easily persisted, transmitted, or logged as JSON.
|
|
403
|
+
|
|
404
|
+
The serialization process is recursive:
|
|
405
|
+
- If a field is a callable (and not a class), it is serialized using cloudpickle.
|
|
406
|
+
- Lists and dictionaries are processed recursively to ensure that all nested callables are properly handled.
|
|
407
|
+
|
|
408
|
+
**Returns:**
|
|
409
|
+
dict[str, Any]: A dictionary representing the FlockAgent, which includes all of its configuration data.
|
|
410
|
+
This dictionary is suitable for storage, debugging, or transmission over the network.
|
|
411
|
+
|
|
412
|
+
**Example:**
|
|
413
|
+
For an agent defined as:
|
|
414
|
+
name = "idea_agent",
|
|
415
|
+
model = "openai/gpt-4o",
|
|
416
|
+
input = "query: str | The search query, context: dict | The full conversation context",
|
|
417
|
+
output = "idea: str | The generated idea"
|
|
418
|
+
Calling `agent.to_dict()` might produce:
|
|
419
|
+
{
|
|
420
|
+
"name": "idea_agent",
|
|
421
|
+
"model": "openai/gpt-4o",
|
|
422
|
+
"input": "query: str | The search query, context: dict | The full conversation context",
|
|
423
|
+
"output": "idea: str | The generated idea",
|
|
424
|
+
"tools": ["<serialized tool representation>"],
|
|
425
|
+
"use_cache": False,
|
|
426
|
+
"hand_off": None,
|
|
427
|
+
"termination": None,
|
|
428
|
+
...
|
|
429
|
+
}
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
def convert_callable(obj: Any) -> Any:
|
|
433
|
+
if callable(obj) and not isinstance(obj, type):
|
|
434
|
+
return cloudpickle.dumps(obj).hex()
|
|
435
|
+
if isinstance(obj, list):
|
|
436
|
+
return [convert_callable(item) for item in obj]
|
|
437
|
+
if isinstance(obj, dict):
|
|
438
|
+
return {k: convert_callable(v) for k, v in obj.items()}
|
|
439
|
+
return obj
|
|
440
|
+
|
|
441
|
+
data = self.dict()
|
|
442
|
+
return convert_callable(data)
|
|
443
|
+
|
|
444
|
+
@classmethod
|
|
445
|
+
def from_dict(cls, data: dict[str, Any]) -> "FlockAgent":
|
|
446
|
+
"""Deserialize a FlockAgent instance from a dictionary.
|
|
447
|
+
|
|
448
|
+
This class method reconstructs a FlockAgent from its serialized dictionary representation, as produced
|
|
449
|
+
by the `to_dict()` method. It recursively processes the dictionary to convert any serialized callables
|
|
450
|
+
(stored as hexadecimal strings via cloudpickle) back into executable callable objects.
|
|
451
|
+
|
|
452
|
+
**Arguments:**
|
|
453
|
+
data (dict[str, Any]): A dictionary representation of a FlockAgent, typically produced by `to_dict()`.
|
|
454
|
+
The dictionary should contain all configuration fields and state information necessary to fully
|
|
455
|
+
reconstruct the agent.
|
|
456
|
+
|
|
457
|
+
**Returns:**
|
|
458
|
+
FlockAgent: An instance of FlockAgent reconstructed from the provided dictionary. The deserialized agent
|
|
459
|
+
will have the same configuration, state, and behavior as the original instance.
|
|
460
|
+
|
|
461
|
+
**Example:**
|
|
462
|
+
Suppose you have the following dictionary:
|
|
463
|
+
{
|
|
464
|
+
"name": "idea_agent",
|
|
465
|
+
"model": "openai/gpt-4o",
|
|
466
|
+
"input": "query: str | The search query, context: dict | The full conversation context",
|
|
467
|
+
"output": "idea: str | The generated idea",
|
|
468
|
+
"tools": ["<serialized tool representation>"],
|
|
469
|
+
"use_cache": False,
|
|
470
|
+
"hand_off": None,
|
|
471
|
+
"termination": None,
|
|
472
|
+
...
|
|
473
|
+
}
|
|
474
|
+
Then, calling:
|
|
475
|
+
agent = FlockAgent.from_dict(data)
|
|
476
|
+
will return a FlockAgent instance with the same properties and behavior as when it was originally serialized.
|
|
477
|
+
"""
|
|
478
|
+
|
|
479
|
+
def convert_callable(obj: Any) -> Any:
|
|
480
|
+
if isinstance(obj, str) and len(obj) > 2:
|
|
481
|
+
try:
|
|
482
|
+
return cloudpickle.loads(bytes.fromhex(obj))
|
|
483
|
+
except Exception:
|
|
484
|
+
return obj
|
|
485
|
+
if isinstance(obj, list):
|
|
486
|
+
return [convert_callable(item) for item in obj]
|
|
487
|
+
if isinstance(obj, dict):
|
|
488
|
+
return {k: convert_callable(v) for k, v in obj.items()}
|
|
489
|
+
return obj
|
|
490
|
+
|
|
491
|
+
converted = convert_callable(data)
|
|
492
|
+
return cls(**converted)
|
flock/core/logging/__init__.py
CHANGED
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
"""Flock logging system with Rich integration and
|
|
2
|
-
|
|
3
|
-
from flock.core.logging.error_handler import error_handler
|
|
4
|
-
from flock.core.logging.formatters import PerformanceFormatter, StructuredFormatter
|
|
5
|
-
from flock.core.logging.handlers import live_update_handler, performance_handler
|
|
6
|
-
from flock.core.logging.logger import flock_logger
|
|
7
|
-
|
|
8
|
-
# Install the Rich error handler by default
|
|
9
|
-
error_handler.install()
|
|
10
|
-
|
|
11
|
-
__all__ = [
|
|
12
|
-
"PerformanceFormatter",
|
|
13
|
-
"StructuredFormatter",
|
|
14
|
-
"error_handler",
|
|
15
|
-
"flock_logger",
|
|
16
|
-
"live_update_handler",
|
|
17
|
-
"performance_handler",
|
|
18
|
-
]
|
|
1
|
+
"""Flock logging system with Rich integration and structured logging support."""
|
|
2
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseFormatter(ABC):
|
|
7
|
+
def __init__(self, max_length: int = 1000) -> None:
|
|
8
|
+
self.max_length = max_length
|
|
9
|
+
|
|
10
|
+
def display(
|
|
11
|
+
self,
|
|
12
|
+
result: dict[str, Any],
|
|
13
|
+
agent_name: str,
|
|
14
|
+
wait: bool = False,
|
|
15
|
+
) -> None:
|
|
16
|
+
self.display_result(result, agent_name)
|
|
17
|
+
if wait:
|
|
18
|
+
input("Press Enter to continue...")
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def display_result(self, result: dict[str, Any], agent_name: str) -> None:
|
|
22
|
+
"""Display an agent's result."""
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def display_data(self, data: dict[str, Any]) -> None:
|
|
27
|
+
"""Display arbitrary data."""
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class FormatterOptions:
|
|
33
|
+
formatter: type[BaseFormatter] = field(default=None)
|
|
34
|
+
wait_for_input: bool = field(default=False)
|
|
35
|
+
max_length: int = field(default=1000)
|
|
36
|
+
settings: Any | None = field(default=None)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from flock.core.logging.formatters.base_formatter import (
|
|
5
|
+
BaseFormatter,
|
|
6
|
+
FormatterOptions,
|
|
7
|
+
)
|
|
8
|
+
from flock.core.logging.formatters.pprint_formatter import PrettyPrintFormatter
|
|
9
|
+
from flock.core.logging.formatters.rich_formatters import RichTables
|
|
10
|
+
from flock.core.logging.formatters.themed_formatter import (
|
|
11
|
+
ThemedAgentResultFormatter,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class FormatterOptions:
|
|
17
|
+
formatter: type[BaseFormatter]
|
|
18
|
+
wait_for_input: bool = False
|
|
19
|
+
settings: dict[str, Any] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FormatterFactory:
|
|
23
|
+
_formatter_map = {
|
|
24
|
+
PrettyPrintFormatter: PrettyPrintFormatter,
|
|
25
|
+
RichTables: RichTables,
|
|
26
|
+
ThemedAgentResultFormatter: ThemedAgentResultFormatter,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def create_formatter(options: FormatterOptions) -> BaseFormatter:
|
|
31
|
+
formatter_cls = options.formatter
|
|
32
|
+
max_length = options.max_length
|
|
33
|
+
if formatter_cls in FormatterFactory._formatter_map:
|
|
34
|
+
formatter = FormatterFactory._formatter_map[formatter_cls]
|
|
35
|
+
if options.settings:
|
|
36
|
+
return formatter(max_length=max_length, **options.settings)
|
|
37
|
+
return formatter(max_length=max_length)
|
|
38
|
+
raise ValueError(f"Unknown formatter: {formatter_cls}")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from flock.core.logging.formatters.base_formatter import BaseFormatter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PrettyPrintFormatter(BaseFormatter):
|
|
7
|
+
def display_result(self, result: dict[str, Any], agent_name: str, **kwargs) -> None:
|
|
8
|
+
"""Print an agent's result using Rich formatting."""
|
|
9
|
+
from devtools import pprint
|
|
10
|
+
|
|
11
|
+
pprint(agent_name)
|
|
12
|
+
pprint(result)
|
|
13
|
+
|
|
14
|
+
def display_data(self, data: dict[str, Any], **kwargs) -> None:
|
|
15
|
+
"""Print an agent's result using Rich formatting."""
|
|
16
|
+
from devtools import pprint
|
|
17
|
+
|
|
18
|
+
pprint(data)
|