Skip to content

Commit 784f99b

Browse files
authored
Merge pull request #32 from ArivunidhiA/feat/add-verbose-flag-23
Add --verbose flag for detailed logging during generation
2 parents 2c6c3df + 27f654e commit 784f99b

4 files changed

Lines changed: 88 additions & 0 deletions

File tree

paperbanana/cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from rich.prompt import Prompt
1414

1515
from paperbanana.core.config import Settings
16+
from paperbanana.core.logging import configure_logging
1617
from paperbanana.core.types import DiagramType, GenerationInput
1718

1819
app = typer.Typer(
@@ -42,8 +43,12 @@ def generate(
4243
None, "--iterations", "-n", help="Refinement iterations"
4344
),
4445
config: Optional[str] = typer.Option(None, "--config", help="Path to config YAML file"),
46+
verbose: bool = typer.Option(
47+
False, "--verbose", "-v", help="Show detailed agent progress and timing"
48+
),
4549
):
4650
"""Generate a methodology diagram from a text description."""
51+
configure_logging(verbose=verbose)
4752
# Load source text
4853
input_path = Path(input)
4954
if not input_path.exists():
@@ -119,8 +124,12 @@ def plot(
119124
output: Optional[str] = typer.Option(None, "--output", "-o", help="Output image path"),
120125
vlm_provider: str = typer.Option("gemini", "--vlm-provider", help="VLM provider"),
121126
iterations: int = typer.Option(3, "--iterations", "-n", help="Number of refinement iterations"),
127+
verbose: bool = typer.Option(
128+
False, "--verbose", "-v", help="Show detailed agent progress and timing"
129+
),
122130
):
123131
"""Generate a statistical plot from data."""
132+
configure_logging(verbose=verbose)
124133
data_path = Path(data)
125134
if not data_path.exists():
126135
console.print(f"[red]Error: Data file not found: {data}[/red]")
@@ -228,8 +237,12 @@ def evaluate(
228237
vlm_provider: str = typer.Option(
229238
"gemini", "--vlm-provider", help="VLM provider for evaluation"
230239
),
240+
verbose: bool = typer.Option(
241+
False, "--verbose", "-v", help="Show detailed agent progress and timing"
242+
),
231243
):
232244
"""Evaluate a generated diagram vs human reference (comparative)."""
245+
configure_logging(verbose=verbose)
233246
from paperbanana.evaluation.judge import VLMJudge
234247

235248
generated_path = Path(generated)

paperbanana/core/logging.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""Logging configuration for PaperBanana."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
import structlog
8+
9+
10+
def configure_logging(*, verbose: bool = False) -> None:
11+
"""Configure structlog output level.
12+
13+
Args:
14+
verbose: If True, show detailed agent progress and timing at DEBUG level.
15+
If False (default), suppress logs below WARNING for clean output.
16+
"""
17+
level = logging.DEBUG if verbose else logging.WARNING
18+
19+
structlog.configure(
20+
wrapper_class=structlog.make_filtering_bound_logger(level),
21+
)

paperbanana/core/pipeline.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ async def generate(self, input: GenerationInput) -> GenerationOutput:
201201
diagram_type=input.diagram_type,
202202
)
203203
retrieval_seconds = time.perf_counter() - retrieval_start
204+
logger.info(
205+
"[Retriever] done",
206+
seconds=round(retrieval_seconds, 1),
207+
examples_found=len(examples),
208+
)
204209

205210
# Step 2: Planner — generate textual description
206211
logger.info("Phase 1: Planning")
@@ -212,6 +217,10 @@ async def generate(self, input: GenerationInput) -> GenerationOutput:
212217
diagram_type=input.diagram_type,
213218
)
214219
planning_seconds = time.perf_counter() - planning_start
220+
logger.info(
221+
"[Planner] done",
222+
seconds=round(planning_seconds, 1),
223+
)
215224

216225
# Step 3: Stylist — optimize description aesthetics
217226
logger.info("Phase 1: Styling")
@@ -224,6 +233,10 @@ async def generate(self, input: GenerationInput) -> GenerationOutput:
224233
diagram_type=input.diagram_type,
225234
)
226235
styling_seconds = time.perf_counter() - styling_start
236+
logger.info(
237+
"[Stylist] done",
238+
seconds=round(styling_seconds, 1),
239+
)
227240

228241
# Save planning outputs
229242
if self.settings.save_iterations:
@@ -254,6 +267,10 @@ async def generate(self, input: GenerationInput) -> GenerationOutput:
254267
iteration=i + 1,
255268
)
256269
visualizer_seconds = time.perf_counter() - visualizer_start
270+
logger.info(
271+
f"[Visualizer] Iteration {i + 1}/{self.settings.refinement_iterations} done",
272+
seconds=round(visualizer_seconds, 1),
273+
)
257274

258275
# Step 5: Critic — evaluate and provide feedback
259276
critic_start = time.perf_counter()
@@ -265,6 +282,11 @@ async def generate(self, input: GenerationInput) -> GenerationOutput:
265282
diagram_type=input.diagram_type,
266283
)
267284
critic_seconds = time.perf_counter() - critic_start
285+
logger.info(
286+
"[Critic] done",
287+
seconds=round(critic_seconds, 1),
288+
needs_revision=critique.needs_revision,
289+
)
268290

269291
iteration_record = IterationRecord(
270292
iteration=i + 1,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Tests for logging configuration."""
2+
3+
from __future__ import annotations
4+
5+
import structlog
6+
7+
from paperbanana.core.logging import configure_logging
8+
9+
10+
def test_configure_logging_default_suppresses_info():
11+
"""Test that default logging sets filtering at WARNING level."""
12+
configure_logging(verbose=False)
13+
logger = structlog.get_logger().bind()
14+
assert "FilteringAtWarning" in type(logger).__name__
15+
16+
17+
def test_configure_logging_verbose_enables_debug():
18+
"""Test that verbose logging sets filtering at DEBUG level."""
19+
configure_logging(verbose=True)
20+
logger = structlog.get_logger().bind()
21+
assert "FilteringAtDebug" in type(logger).__name__
22+
23+
24+
def test_configure_logging_verbose_false_then_true():
25+
"""Test that logging can be reconfigured from quiet to verbose."""
26+
configure_logging(verbose=False)
27+
logger = structlog.get_logger().bind()
28+
assert "FilteringAtWarning" in type(logger).__name__
29+
30+
configure_logging(verbose=True)
31+
logger = structlog.get_logger().bind()
32+
assert "FilteringAtDebug" in type(logger).__name__

0 commit comments

Comments
 (0)