Bellok commited on
Commit
e7d33ec
·
1 Parent(s): 9cb8f84

feat: Implement document synthesis endpoint with hierarchical summarization

Browse files
Files changed (2) hide show
  1. app.py +128 -24
  2. warbler_cda/api/service.py +34 -94
app.py CHANGED
@@ -9,9 +9,11 @@ Provides a web UI for the FractalStat RAG system with GPU acceleration.
9
  import gradio as gr
10
  import os
11
  import time
 
12
 
13
  from warbler_cda.answer_generator import AnswerGenerator
14
  from warbler_cda.remote_pack_loader import RemotePackLoader
 
15
 
16
  # Import the HuggingFace Spaces GPU decorator
17
  try:
@@ -401,6 +403,78 @@ def get_system_stats() -> str:
401
  return output
402
 
403
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  # Create Gradio interface
405
  with gr.Blocks(title="Warbler CDA - FractalStat RAG") as demo:
406
  gr.Markdown("""
@@ -598,36 +672,66 @@ with gr.Blocks(title="Warbler CDA - FractalStat RAG") as demo:
598
  )
599
 
600
  with gr.Tab("System Stats"):
601
- stats_output = gr.Markdown(get_system_stats())
602
  stats_btn = gr.Button("Refresh Stats")
603
  def refresh_stats():
604
  return get_system_stats()
605
  stats_btn.click(fn=refresh_stats, outputs=stats_output)
606
- demo.load(fn=refresh_stats, outputs=stats_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
  with gr.Tab("About"):
609
- gr.Markdown("""
610
- ## About Warbler CDA
611
-
612
- Warbler CDA is a production-ready RAG system featuring:
613
-
614
- - **8D FractalStat Addressing**: Multi-dimensional intelligence for superior retrieval
615
- - **Semantic Anchors**: Persistent memory with provenance tracking
616
- - **Bob the Skeptic**: Automatic bias detection and validation
617
- - **Narrative Coherence**: Quality analysis beyond simple similarity
618
-
619
- ### Performance
620
-
621
- - 84% test coverage with 587 passing tests
622
- - 9-28s query response time
623
- - 0.88 average relevance score
624
- - 75-83% narrative coherence
625
-
626
- ### Links
627
-
628
- - [Source Code](https://gitlab.com/tiny-walnut-games/the-seed)
629
- - [Documentation](https://gitlab.com/tiny-walnut-games/the-seed/-/tree/main/warbler-cda-package)
630
- - [Performance Report](https://gitlab.com/tiny-walnut-games/the-seed/-/blob/main/warbler-cda-package/WARBLER_CDA_PERFORMANCE_REPORT.md)
631
  """)
632
 
633
  if __name__ == "__main__":
 
9
  import gradio as gr
10
  import os
11
  import time
12
+ from typing import List, Dict, Any
13
 
14
  from warbler_cda.answer_generator import AnswerGenerator
15
  from warbler_cda.remote_pack_loader import RemotePackLoader
16
+ from warbler_cda.summarization_ladder import SummarizationLadder
17
 
18
  # Import the HuggingFace Spaces GPU decorator
19
  try:
 
403
  return output
404
 
405
 
406
+ def synthesize_document(
407
+ document_text: str,
408
+ micro_window_size: int = 5,
409
+ macro_trigger_count: int = 3,
410
+ ) -> str:
411
+ """Generate a hierarchical synthesis artifact for a document-sized input."""
412
+ cleaned = document_text.strip()
413
+ if not cleaned:
414
+ return "Please provide document text to synthesize."
415
+
416
+ fragments: List[Dict[str, Any]] = []
417
+ units = [part.strip() for part in cleaned.splitlines() if part.strip()]
418
+ if len(units) < 2:
419
+ units = [sentence.strip() for sentence in cleaned.split(".") if sentence.strip()]
420
+
421
+ for index, unit in enumerate(units, start=1):
422
+ fragments.append(
423
+ {
424
+ "id": f"fragment_{index}",
425
+ "text": unit,
426
+ "heat": min(1.0, 0.4 + (len(unit) / 400.0)),
427
+ }
428
+ )
429
+
430
+ if not fragments:
431
+ return "Unable to derive synthesis fragments from the provided text."
432
+
433
+ ladder = SummarizationLadder(
434
+ config={
435
+ "micro_window_size": micro_window_size,
436
+ "macro_trigger_count": macro_trigger_count,
437
+ }
438
+ )
439
+ report = ladder.process_fragments(fragments)
440
+
441
+ lines = ["## Synthesis Output", "", f"**Input Fragments:** {len(fragments)}", ""]
442
+
443
+ if ladder.macro_distillations:
444
+ latest_macro = ladder.macro_distillations[-1]
445
+ lines.extend([
446
+ "### Macro Distillation",
447
+ "",
448
+ latest_macro.distilled_essence,
449
+ "",
450
+ ])
451
+
452
+ if ladder.micro_summaries:
453
+ lines.append("### Micro Summaries")
454
+ lines.append("")
455
+ for micro in list(ladder.micro_summaries)[-3:]:
456
+ lines.append(f"- {micro.compressed_text}")
457
+ lines.append("")
458
+
459
+ metrics = ladder.get_compression_metrics()["current_state"]
460
+ lines.extend([
461
+ "### Metrics",
462
+ "",
463
+ f"- Active Micro Summaries: {metrics['micro_summaries_active']}",
464
+ f"- Macro Distillations: {metrics['macro_distillations_total']}",
465
+ f"- Compression Ratio: {metrics['compression_ratio']:.2f}",
466
+ "",
467
+ ])
468
+
469
+ if report["new_macro_distillations"]:
470
+ lines.append("### New Distillation IDs")
471
+ lines.append("")
472
+ for item in report["new_macro_distillations"]:
473
+ lines.append(f"- {item['distillation_id']}")
474
+
475
+ return "\n".join(lines)
476
+
477
+
478
  # Create Gradio interface
479
  with gr.Blocks(title="Warbler CDA - FractalStat RAG") as demo:
480
  gr.Markdown("""
 
672
  )
673
 
674
  with gr.Tab("System Stats"):
675
+ stats_output = gr.Markdown("Click **Refresh Stats** to load system metrics.")
676
  stats_btn = gr.Button("Refresh Stats")
677
  def refresh_stats():
678
  return get_system_stats()
679
  stats_btn.click(fn=refresh_stats, outputs=stats_output)
680
+
681
+ with gr.Tab("Synthesis"):
682
+ synthesis_input = gr.Textbox(
683
+ label="Document Text",
684
+ placeholder="Paste notes, docs, or source material to produce a hierarchical synthesis...",
685
+ lines=12,
686
+ )
687
+ with gr.Row():
688
+ synthesis_micro_window = gr.Slider(
689
+ minimum=2,
690
+ maximum=8,
691
+ value=5,
692
+ step=1,
693
+ label="Micro Window Size",
694
+ )
695
+ synthesis_macro_trigger = gr.Slider(
696
+ minimum=2,
697
+ maximum=5,
698
+ value=3,
699
+ step=1,
700
+ label="Macro Trigger Count",
701
+ )
702
+ synthesis_btn = gr.Button("Synthesize", variant="primary")
703
+ synthesis_output = gr.Markdown(
704
+ "Paste a document and click **Synthesize** to generate a TLDA-style macro distillation."
705
+ )
706
+ synthesis_btn.click(
707
+ fn=synthesize_document,
708
+ inputs=[synthesis_input, synthesis_micro_window, synthesis_macro_trigger],
709
+ outputs=synthesis_output,
710
+ )
711
 
712
  with gr.Tab("About"):
713
+ gr.HTML("""
714
+ <h2>About Warbler CDA</h2>
715
+ <p>Warbler CDA is a production-ready RAG system featuring:</p>
716
+ <ul>
717
+ <li><strong>8D FractalStat Addressing</strong>: Multi-dimensional intelligence for superior retrieval</li>
718
+ <li><strong>Semantic Anchors</strong>: Persistent memory with provenance tracking</li>
719
+ <li><strong>Bob the Skeptic</strong>: Automatic bias detection and validation</li>
720
+ <li><strong>Narrative Coherence</strong>: Quality analysis beyond simple similarity</li>
721
+ </ul>
722
+ <h3>Performance</h3>
723
+ <ul>
724
+ <li>84% test coverage with 587 passing tests</li>
725
+ <li>9-28s query response time</li>
726
+ <li>0.88 average relevance score</li>
727
+ <li>75-83% narrative coherence</li>
728
+ </ul>
729
+ <h3>Links</h3>
730
+ <ul>
731
+ <li><a href="https://gitlab.com/tiny-walnut-games/the-seed" target="_blank">Source Code</a></li>
732
+ <li><a href="https://gitlab.com/tiny-walnut-games/the-seed/-/tree/main/warbler-cda-package" target="_blank">Documentation</a></li>
733
+ <li><a href="https://gitlab.com/tiny-walnut-games/the-seed/-/blob/main/warbler-cda-package/WARBLER_CDA_PERFORMANCE_REPORT.md" target="_blank">Performance Report</a></li>
734
+ </ul>
735
  """)
736
 
737
  if __name__ == "__main__":
warbler_cda/api/service.py CHANGED
@@ -1,60 +1,3 @@
1
- # --- TLDA-style Synthesis Imports and Endpoint (at end of file) ---
2
- from warbler_cda.summarization_ladder import SummarizationLadder
3
- from typing import List, Dict, Any, Optional
4
- from pydantic import BaseModel
5
-
6
- # Synthesis pipeline instance (lazy init)
7
- _synthesis_ladder: Optional[SummarizationLadder] = None
8
-
9
- class SynthesisRequest(BaseModel):
10
- """Request model for synthesis (TLDA-style)"""
11
- fragments: List[Dict[str, Any]]
12
- config: Optional[Dict[str, Any]] = None
13
-
14
- class SynthesisResult(BaseModel):
15
- """Response model for synthesis results"""
16
- micro_summaries: List[Dict[str, Any]]
17
- macro_distillations: List[Dict[str, Any]]
18
- metrics: Dict[str, Any]
19
-
20
- def _init_synthesis_ladder(config=None):
21
- global _synthesis_ladder
22
- if _synthesis_ladder is None:
23
- _synthesis_ladder = SummarizationLadder(config=config)
24
- return _synthesis_ladder
25
-
26
- @app.post("/synthesize", response_model=SynthesisResult)
27
- async def synthesize(request: SynthesisRequest):
28
- """Synthesize a document or fragment list using the SummarizationLadder pipeline."""
29
- ladder = _init_synthesis_ladder(request.config)
30
- report = ladder.process_fragments(request.fragments)
31
- # Collect micro/macro summaries for response
32
- micro_summaries = [
33
- {
34
- "summary_id": ms.summary_id,
35
- "compressed_text": ms.compressed_text,
36
- "window_size": ms.window_size,
37
- "heat_aggregate": ms.heat_aggregate,
38
- "age_seconds": ms.get_age_seconds(),
39
- }
40
- for ms in list(ladder.micro_summaries)
41
- ]
42
- macro_distillations = [
43
- {
44
- "distillation_id": md.distillation_id,
45
- "distilled_essence": md.distilled_essence,
46
- "consolidation_ratio": md.consolidation_ratio,
47
- "anchor_reinforcements": md.anchor_reinforcements,
48
- }
49
- for md in ladder.macro_distillations
50
- ]
51
- metrics = ladder.get_compression_metrics()
52
- return SynthesisResult(
53
- micro_summaries=micro_summaries,
54
- macro_distillations=macro_distillations,
55
- metrics=metrics,
56
- )
57
-
58
  """
59
  EXP-09 CLI API Service - FractalStat Retrieval API with Concurrency Support.
60
 
@@ -77,6 +20,7 @@ from warbler_cda.retrieval_api import RetrievalAPI, RetrievalQuery, RetrievalMod
77
  from warbler_cda.fractalstat_rag_bridge import FractalStatRAGBridge
78
  from warbler_cda.pack_loader import PackLoader
79
  from warbler_cda.answer_generator import AnswerGenerator
 
80
 
81
  # Configure logging
82
  logging.basicConfig(level=logging.INFO)
@@ -101,11 +45,6 @@ app = FastAPI(
101
  lifespan=lifespan,
102
  )
103
 
104
- # --- TLDA-style Synthesis Imports and Endpoint (immediately after app definition) ---
105
- from warbler_cda.summarization_ladder import SummarizationLadder
106
- from typing import List, Dict, Any, Optional
107
- from pydantic import BaseModel
108
-
109
  # Synthesis pipeline instance (lazy init)
110
  _synthesis_ladder: Optional[SummarizationLadder] = None
111
 
@@ -126,38 +65,6 @@ def _init_synthesis_ladder(config=None):
126
  _synthesis_ladder = SummarizationLadder(config=config)
127
  return _synthesis_ladder
128
 
129
- @app.post("/synthesize", response_model=SynthesisResult)
130
- async def synthesize(request: SynthesisRequest):
131
- """Synthesize a document or fragment list using the SummarizationLadder pipeline."""
132
- ladder = _init_synthesis_ladder(request.config)
133
- report = ladder.process_fragments(request.fragments)
134
- # Collect micro/macro summaries for response
135
- micro_summaries = [
136
- {
137
- "summary_id": ms.summary_id,
138
- "compressed_text": ms.compressed_text,
139
- "window_size": ms.window_size,
140
- "heat_aggregate": ms.heat_aggregate,
141
- "age_seconds": ms.get_age_seconds(),
142
- }
143
- for ms in list(ladder.micro_summaries)
144
- ]
145
- macro_distillations = [
146
- {
147
- "distillation_id": md.distillation_id,
148
- "distilled_essence": md.distilled_essence,
149
- "consolidation_ratio": md.consolidation_ratio,
150
- "anchor_reinforcements": md.anchor_reinforcements,
151
- }
152
- for md in ladder.macro_distillations
153
- ]
154
- metrics = ladder.get_compression_metrics()
155
- return SynthesisResult(
156
- micro_summaries=micro_summaries,
157
- macro_distillations=macro_distillations,
158
- metrics=metrics,
159
- )
160
-
161
  # Global state
162
  _api_instance: Optional[RetrievalAPI] = None
163
  _answer_generator: Optional[AnswerGenerator] = None
@@ -891,6 +798,39 @@ async def reset_metrics():
891
  return {"status": "metrics reset"}
892
 
893
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
  if __name__ == "__main__":
895
  import uvicorn
896
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  EXP-09 CLI API Service - FractalStat Retrieval API with Concurrency Support.
3
 
 
20
  from warbler_cda.fractalstat_rag_bridge import FractalStatRAGBridge
21
  from warbler_cda.pack_loader import PackLoader
22
  from warbler_cda.answer_generator import AnswerGenerator
23
+ from warbler_cda.summarization_ladder import SummarizationLadder
24
 
25
  # Configure logging
26
  logging.basicConfig(level=logging.INFO)
 
45
  lifespan=lifespan,
46
  )
47
 
 
 
 
 
 
48
  # Synthesis pipeline instance (lazy init)
49
  _synthesis_ladder: Optional[SummarizationLadder] = None
50
 
 
65
  _synthesis_ladder = SummarizationLadder(config=config)
66
  return _synthesis_ladder
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  # Global state
69
  _api_instance: Optional[RetrievalAPI] = None
70
  _answer_generator: Optional[AnswerGenerator] = None
 
798
  return {"status": "metrics reset"}
799
 
800
 
801
+ @app.post("/synthesize", response_model=SynthesisResult)
802
+ async def synthesize(request: SynthesisRequest):
803
+ """Synthesize fragments through the hierarchical summarization ladder."""
804
+ ladder = _init_synthesis_ladder(request.config)
805
+ ladder.process_fragments(request.fragments)
806
+
807
+ micro_summaries = [
808
+ {
809
+ "summary_id": summary.summary_id,
810
+ "compressed_text": summary.compressed_text,
811
+ "window_size": summary.window_size,
812
+ "heat_aggregate": summary.heat_aggregate,
813
+ "age_seconds": summary.get_age_seconds(),
814
+ }
815
+ for summary in list(ladder.micro_summaries)
816
+ ]
817
+ macro_distillations = [
818
+ {
819
+ "distillation_id": distillation.distillation_id,
820
+ "distilled_essence": distillation.distilled_essence,
821
+ "consolidation_ratio": distillation.consolidation_ratio,
822
+ "anchor_reinforcements": distillation.anchor_reinforcements,
823
+ }
824
+ for distillation in ladder.macro_distillations
825
+ ]
826
+
827
+ return SynthesisResult(
828
+ micro_summaries=micro_summaries,
829
+ macro_distillations=macro_distillations,
830
+ metrics=ladder.get_compression_metrics(),
831
+ )
832
+
833
+
834
  if __name__ == "__main__":
835
  import uvicorn
836