Skip to content

Commit df12d44

Browse files
authored
fix(da-layer): update da layer (flare-foundation#103)
2 parents 5ab6a9a + a50a3c1 commit df12d44

18 files changed

+435
-892
lines changed
File renamed without changes.

examples/02_da_layer_usage.py

100755100644
Lines changed: 44 additions & 356 deletions
Large diffs are not rendered by default.

examples/02_ingest_pdf.py

Lines changed: 0 additions & 95 deletions
This file was deleted.
File renamed without changes.

examples/05_ingest_pdf.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
import json
5+
import re
6+
from pathlib import Path
7+
from typing import Any
8+
from unittest.mock import AsyncMock, mock_open, patch
9+
10+
from data.create_sample_invoice import create_invoice_and_build_template
11+
from google.adk.runners import Runner
12+
from google.adk.sessions import InMemorySessionService
13+
from google.genai import types
14+
15+
from flare_ai_kit import FlareAIKit
16+
from flare_ai_kit.agent.adk_agent import pdf_agent
17+
from flare_ai_kit.config import AppSettings
18+
from flare_ai_kit.ingestion.settings import (
19+
IngestionSettings,
20+
OnchainContractSettings,
21+
PDFIngestionSettings,
22+
PDFTemplateSettings,
23+
)
24+
25+
MOCK_TX_HASH = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
26+
27+
28+
def _prompt(pdf: Path, template: PDFTemplateSettings, max_pages: int | None) -> str:
29+
"""Build the prompt from the template."""
30+
return (
31+
"Parse this PDF using tools and return ONLY JSON per the template.\n"
32+
f"PDF_PATH: {pdf}\nMAX_PAGES: {max_pages or 'ALL'}\n\n"
33+
"TEMPLATE_JSON:\n```json\n" + json.dumps(template.model_dump()) + "\n```\n\n"
34+
"- Call read_pdf_text(file_path=PDF_PATH, max_pages=MAX_PAGES).\n"
35+
"- Extract each field in TEMPLATE_JSON.fields.\n"
36+
"- Reply with a single JSON object (no markdown)."
37+
)
38+
39+
40+
def _json_from(text: str) -> dict[str, Any]:
41+
"""Extract JSON from agent return text."""
42+
try:
43+
return json.loads(text)
44+
except json.JSONDecodeError as e:
45+
fence = re.search(
46+
r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL | re.IGNORECASE
47+
)
48+
if fence:
49+
return json.loads(fence.group(1))
50+
blob = re.search(r"\{.*\}", text, re.DOTALL)
51+
if blob:
52+
return json.loads(blob.group(0))
53+
msg = f"Agent response is not valid JSON:\n{text}"
54+
raise RuntimeError(msg) from e
55+
56+
57+
async def parse_pdf_to_template_json(
58+
pdf: str | Path, template: PDFTemplateSettings, max_pages: int | None = None
59+
) -> dict[str, Any]:
60+
"""Setup in-memory ADK agent, give it the PDF, template and prompt."""
61+
pdf = Path(pdf)
62+
svc = InMemorySessionService()
63+
await svc.create_session(app_name="app", user_id="u", session_id="s")
64+
runner = Runner(agent=pdf_agent, app_name="app", session_service=svc)
65+
66+
msg = types.Content(
67+
role="user", parts=[types.Part(text=_prompt(pdf, template, max_pages))]
68+
)
69+
final_text = None
70+
async for ev in runner.run_async(user_id="u", session_id="s", new_message=msg):
71+
if ev.is_final_response() and ev.content and ev.content.parts:
72+
final_text = ev.content.parts[0].text
73+
break
74+
if not final_text:
75+
msg = "Agent produced no response."
76+
raise RuntimeError(msg)
77+
return _json_from(final_text)
78+
79+
80+
async def main() -> None:
81+
# Create PDF and save it
82+
pdf_path, template = create_invoice_and_build_template("generated_invoice")
83+
84+
# Add template to global settings
85+
app_settings = AppSettings(
86+
log_level="INFO",
87+
ingestion=IngestionSettings(
88+
pdf_ingestion=PDFIngestionSettings(
89+
templates=[template],
90+
use_ocr=False,
91+
contract_settings=OnchainContractSettings(
92+
contract_address="0x0000000000000000000000000000000000000000",
93+
abi_name="OnchainDataRegistry",
94+
function_name="registerDocument",
95+
),
96+
)
97+
),
98+
)
99+
100+
# Mock onchain contract posting
101+
with (
102+
patch(
103+
"flare_ai_kit.onchain.contract_poster.ContractPoster.post_data",
104+
new_callable=AsyncMock,
105+
return_value=MOCK_TX_HASH,
106+
) as mock_post,
107+
patch("flare_ai_kit.onchain.contract_poster.open", mock_open(read_data="[]")),
108+
):
109+
kit = FlareAIKit(config=app_settings)
110+
tx_hash = await kit.pdf_processor.ingest_and_post(
111+
file_path=str(pdf_path), template_name=template.template_name
112+
)
113+
print("✅ on-chain tx:", tx_hash)
114+
print("📄 extracted:", mock_post.call_args[0][0])
115+
116+
# Agent PDF parsing
117+
structured = await parse_pdf_to_template_json(pdf_path, template, max_pages=1)
118+
print("🧩 agent JSON:", json.dumps(structured, indent=2))
119+
120+
121+
if __name__ == "__main__":
122+
asyncio.run(main())

0 commit comments

Comments
 (0)