Skip to content

Commit

Permalink
Merge pull request #375 from PrefectHQ/update-print-setting
Browse files Browse the repository at this point in the history
Update pretty-print setting name
  • Loading branch information
jlowin authored Nov 5, 2024
2 parents 33a40cd + fd9f6b7 commit dc428d6
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 8 deletions.
2 changes: 1 addition & 1 deletion docs/guides/settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ cf.settings.log_level = 'DEBUG'
Default: `False`
- `log_all_messages`: If True, all LLM messages will be logged at the debug level.
Default: `False`
- `pretty_print_agent_events`: If True, a PrintHandler will be enabled and
- `enable_default_print_handler`: If True, a PrintHandler will be enabled and
automatically pretty-print agent events. Note that this may interfere with logging.
Default: `True`

Expand Down
2 changes: 1 addition & 1 deletion docs/patterns/running-tasks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ Handlers in ControlFlow provide a way to observe and react to events that occur

ControlFlow supports both synchronous and asynchronous handlers. Synchronous handlers implement the `Handler` interface, while asynchronous handlers implement the `AsyncHandler` interface. Both interfaces define methods for various events that can occur during task execution, including agent messages (and message deltas), user messages, tool calls, tool results, orchestrator sessions starting or stopping, and more.

ControlFlow includes a built-in `PrintHandler` that pretty-prints agent responses and tool calls to the terminal. It's used by default if `controlflow.settings.pretty_print_agent_events=True` and no other handlers are provided.
ControlFlow includes a built-in `PrintHandler` that pretty-prints agent responses and tool calls to the terminal. It's used by default if `controlflow.settings.enable_default_print_handler=True` and no other handlers are provided.

### How handlers work

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ env = [
# use 4o-mini for tests by default
'D:CONTROLFLOW_LLM_MODEL=openai/gpt-4o-mini',
'D:CONTROLFLOW_TOOLS_VERBOSE=1',
'D:CONTROLFLOW_PRETTY_PRINT_AGENT_EVENTS=0',
'D:CONTROLFLOW_ENABLE_DEFAULT_PRINT_HANDLER=0',
'D:CONTROLFLOW_LOG_LEVEL=DEBUG',
'D:PREFECT_LOGGING_LEVEL=DEBUG',
]
Expand Down
2 changes: 1 addition & 1 deletion src/controlflow/orchestration/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def _validate_handlers(cls, v):
"""
from controlflow.orchestration.print_handler import PrintHandler

if v is None and controlflow.settings.pretty_print_agent_events:
if v is None and controlflow.settings.enable_default_print_handler:
v = [PrintHandler()]
return v or []

Expand Down
9 changes: 8 additions & 1 deletion src/controlflow/orchestration/print_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@


class PrintHandler(Handler):
def __init__(self):
def __init__(self, include_completion_tools: bool = True):
self.events: dict[str, Event] = {}
self.paused_id: str = None
self.include_completion_tools = include_completion_tools
super().__init__()

def update_live(self, latest: BaseMessage = None):
Expand Down Expand Up @@ -91,6 +92,12 @@ def on_tool_call(self, event: ToolCallEvent):
self.events.clear()

def on_tool_result(self, event: ToolResultEvent):
# skip completion tools if configured to do so
if not self.include_completion_tools and event.tool_result.tool_metadata.get(
"is_completion_tool"
):
return

self.events[f"tool-result:{event.tool_call['id']}"] = event

# # if we were paused, resume the live display
Expand Down
25 changes: 24 additions & 1 deletion src/controlflow/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
import os
from contextlib import contextmanager
from pathlib import Path
from pyexpat import model
from typing import Any, Literal, Optional, Union

import prefect.logging.configuration
import prefect.settings
from pydantic import Field, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

from controlflow.utilities.general import unwrap

CONTROLFLOW_ENV_FILE = os.getenv("CONTROLFLOW_ENV_FILE", "~/.controlflow/.env")


Expand Down Expand Up @@ -47,7 +50,27 @@ class Settings(ControlFlowSettings):
default=False,
description="If True, all LLM messages will be logged at the debug level.",
)
pretty_print_agent_events: bool = Field(
pretty_print_agent_events: Optional[bool] = Field(
default=None,
description="If True, agent events will be pretty-printed.",
deprecated=True,
)

@model_validator(mode="before")
def _validate_pretty_print_agent_events(cls, data: dict) -> dict:
if data.get("pretty_print_agent_events") is not None:
data["enable_default_print_handler"] = data["pretty_print_agent_events"]
data["pretty_print_agent_events"] = None
print(
unwrap("""
The `pretty_print_agent_events` setting is deprecated, use
`enable_default_print_handler` instead. Your settings were
updated.
""")
)
return data

enable_default_print_handler: bool = Field(
default=True,
description="If True, a PrintHandler will be enabled and automatically "
"pretty-print agent events. Note that this may interfere with logging.",
Expand Down
4 changes: 4 additions & 0 deletions src/controlflow/tasks/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ def get_success_tool(self) -> Tool:
"""
options = {}
instructions = []
metadata = {"is_completion_tool": True}
result_schema = None

# if the result_type is a tuple of options, then we want the LLM to provide
Expand Down Expand Up @@ -648,6 +649,7 @@ def succeed(**kwargs) -> str:
description=f"Mark task {self.id} as successful.",
instructions="\n\n".join(instructions) or None,
parameters=result_schema.model_json_schema(),
metadata=metadata,
)

# for all other results, we create a single `result` kwarg to capture the result
Expand All @@ -666,6 +668,7 @@ def succeed(**kwargs) -> str:
description=f"Mark task {self.id} as successful.",
instructions="\n\n".join(instructions) or None,
include_return_description=False,
metadata=metadata,
)
def succeed(task_result: result_schema) -> str: # type: ignore
if self.is_successful():
Expand All @@ -690,6 +693,7 @@ def succeed(task_result: result_schema) -> str: # type: ignore
description=f"Mark task {self.id} as successful.",
instructions="\n\n".join(instructions) or None,
include_return_description=False,
metadata=metadata,
)
def succeed() -> str:
self.mark_successful()
Expand Down
9 changes: 8 additions & 1 deletion src/controlflow/tools/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def from_function(
instructions: Optional[str] = None,
include_param_descriptions: bool = True,
include_return_description: bool = True,
metadata: Optional[dict] = None,
**kwargs,
):
name = name or fn.__name__
Expand Down Expand Up @@ -190,6 +191,7 @@ def from_function(
parameters=parameters,
fn=fn,
instructions=instructions,
metadata=metadata or {},
**kwargs,
)

Expand All @@ -205,7 +207,7 @@ def from_lc_tool(cls, tool: langchain_core.tools.BaseTool, **kwargs):
)

def serialize_for_prompt(self) -> dict:
return self.model_dump(include={"name", "description"})
return self.model_dump(include={"name", "description", "metadata"})


def tool(
Expand All @@ -216,6 +218,7 @@ def tool(
instructions: Optional[str] = None,
include_param_descriptions: bool = True,
include_return_description: bool = True,
metadata: Optional[dict] = None,
**kwargs,
) -> Tool:
"""
Expand All @@ -225,6 +228,7 @@ def tool(
instructions=instructions,
include_param_descriptions=include_param_descriptions,
include_return_description=include_return_description,
metadata=metadata or {},
)
if fn is None:
return functools.partial(tool, name=name, description=description, **kwargs)
Expand Down Expand Up @@ -298,6 +302,7 @@ class ToolResult(ControlFlowModel):
result: Any = Field(exclude=True, repr=False)
str_result: str = Field(repr=False)
is_error: bool = False
tool_metadata: dict = {}


def handle_tool_call(
Expand Down Expand Up @@ -339,6 +344,7 @@ def handle_tool_call(
result=fn_output,
str_result=output_to_string(fn_output),
is_error=is_error,
tool_metadata=tool.metadata if tool else {},
)


Expand Down Expand Up @@ -379,4 +385,5 @@ async def handle_tool_call_async(tool_call: ToolCall, tools: list[Tool]) -> Any:
result=fn_output,
str_result=output_to_string(fn_output),
is_error=is_error,
tool_metadata=tool.metadata if tool else {},
)
2 changes: 1 addition & 1 deletion tests/fixtures/controlflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@pytest.fixture(autouse=True, scope="session")
def temp_controlflow_settings():
with temporary_settings(
pretty_print_agent_events=False,
enable_default_print_handler=False,
log_all_messages=True,
log_level="DEBUG",
orchestrator_max_agent_turns=10,
Expand Down
1 change: 1 addition & 0 deletions tests/utilities/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ def test_record_task_events(default_fake_llm):
tool_call_id="call_ZEPdV8mCgeBe5UHjKzm6e3pe",
str_result='Task #12345 ("say hello") marked successful.',
is_error=False,
tool_metadata={"is_completion_tool": True},
)

0 comments on commit dc428d6

Please sign in to comment.