From 0e70ae244069234d826817c46a9f730927ee6e99 Mon Sep 17 00:00:00 2001 From: Jinsoo Shin <59598545+jsshin2022@users.noreply.github.com> Date: Thu, 24 Sep 2026 11:36:02 +0900 Subject: [PATCH 1/2] feat(agent): six-step agent flow, Streamlit Flow page, group guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - core/agent: define_problem → collect → structure_metrics → estimate → guard → report, runnable offline (python) or via LangGraph - pre-registration gate: plan.yaml must be committed before running - guard: abstention verdicts + overclaim linter on report text - run_manifest.json for reproducibility; log paths relative to the case - app/pages/1_Flow.py: run or replay a flow with per-step results - docs/ops/group-guide.md: roles, weekly milestones, branch/PR rules - tests run the example case in a temp copy (no tracked-file churn) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EMnmZPBD5PQJW1UQAARN7q --- .github/workflows/ci.yml | 16 +- Makefile | 7 +- README.md | 23 +- app/flow_view.py | 277 ++++++++++++++++++ app/pages/1_Flow.py | 61 ++++ core/agent/__init__.py | 51 ++-- core/agent/__main__.py | 37 +++ core/agent/graph.py | 85 ++++++ core/agent/guard.py | 98 +++++++ core/agent/llm.py | 46 +++ core/agent/nodes.py | 373 +++++++++++++++++++++++++ core/agent/state.py | 135 +++++++++ core/agent/tools.py | 25 ++ core/report/markdown.py | 7 +- core/schema/plan.py | 2 + docs/ops/group-guide.md | 57 ++++ tests/core/test_agent.py | 146 ++++++++++ tests/core/test_plan_and_case.py | 12 +- tests/fixtures/flow_case/flow_log.json | 225 +++++++++++++++ tests/fixtures/flow_case/plan.yaml | 10 + tests/fixtures/flow_case/report.md | 3 + tests/test_app_flow.py | 73 +++++ 22 files changed, 1733 insertions(+), 36 deletions(-) create mode 100644 app/flow_view.py create mode 100644 app/pages/1_Flow.py create mode 100644 core/agent/__main__.py create mode 100644 core/agent/graph.py create mode 100644 core/agent/guard.py create mode 100644 core/agent/llm.py create mode 100644 core/agent/nodes.py create mode 100644 core/agent/state.py create mode 100644 core/agent/tools.py create mode 100644 docs/ops/group-guide.md create mode 100644 tests/core/test_agent.py create mode 100644 tests/fixtures/flow_case/flow_log.json create mode 100644 tests/fixtures/flow_case/plan.yaml create mode 100644 tests/fixtures/flow_case/report.md create mode 100644 tests/test_app_flow.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ddff42..3ffd301 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,24 @@ jobs: - name: Install run: | python -m pip install --upgrade pip - pip install -e ".[dev]" + pip install -e ".[dev,agent]" - name: Ruff run: | ruff check . ruff format --check . || echo "::warning::ruff format differences (run 'ruff format .')" - name: Pytest run: pytest -q + - name: Example flow (offline) + run: make flow CASE=cases/_example_night_clinic + - name: Upload flow artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: example-flow + path: | + cases/_example_night_clinic/flow_log.json + cases/_example_night_clinic/run_manifest.json + cases/_example_night_clinic/report.md + cases/_example_night_clinic/figures/ + if-no-files-found: warn + retention-days: 14 diff --git a/Makefile b/Makefile index 418f7e6..e8eb75f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: install check lint test format app activity +.PHONY: install check lint test format app activity flow PY ?= python @@ -19,5 +19,10 @@ format: app: streamlit run app/streamlit_app.py +# Run the 6-step agent flow on a case (demo: skips the plan pre-registration gate). +CASE ?= cases/_example_night_clinic +flow: + $(PY) -m core.agent $(CASE) --allow-uncommitted + activity: $(PY) scripts/weekly_activity.py --days 7 diff --git a/README.md b/README.md index 9a69322..67a8548 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ cases/ _example_*/ 참고용 예시 케이스 <조-주제>/ 조별 케이스 (plan.yaml, fetch.py, estimate.py, report.md, figures/) app/streamlit_app.py 케이스 브라우저 (API 키 없이 실행) -docs/ops/ GitHub 온보딩, 모니터링 가이드 +docs/ops/ 조별 운영 가이드, GitHub 온보딩, 모니터링 docs/strategy/ 문제 정의·전략 문서 scripts/ 운영 스크립트 (weekly_activity.py 등) tests/ 테스트 @@ -60,7 +60,7 @@ cp -r cases/_template cases/group3-youth-rent # 폴더명: <조>-<주제>, 소 3. **추정** — `estimate.py`: `core.estimators`로 효과 추정 + 반증(placebo 등) → `figures/*.png` 4. **리포트** — `report.md`: 결과·한계·정책 시사점. `make app`에서 바로 보입니다. -자세한 절차: [`cases/_template/README.md`](cases/_template/README.md), 협업 규칙: [`CONTRIBUTING.md`](CONTRIBUTING.md), GitHub가 처음이라면: [`docs/ops/github-onboarding.md`](docs/ops/github-onboarding.md) +자세한 절차: [`cases/_template/README.md`](cases/_template/README.md), 협업 규칙: [`CONTRIBUTING.md`](CONTRIBUTING.md), GitHub가 처음이라면: [`docs/ops/github-onboarding.md`](docs/ops/github-onboarding.md), 조별 운영: [`docs/ops/group-guide.md`](docs/ops/group-guide.md) ## 7주 로드맵 @@ -74,6 +74,25 @@ cp -r cases/_template cases/group3-youth-rent # 폴더명: <조>-<주제>, 소 | 6 | 리포트·플랫폼 | `report.md`, Streamlit 반영 | | 7 | 발표·회고·공개 정리 | 최종 PR 머지, 릴리스 태그 | +## Flow — 6단계 에이전트 흐름 + +`core.agent`가 케이스 하나를 아래 6단계로 실행하고 `cases/<케이스>/flow_log.json`에 단계별 결과를 남깁니다. 앱의 **Flow** 페이지(`make app` → 사이드바 Flow)에서 실행하거나 저장된 로그를 볼 수 있습니다. + +| 단계 | 하는 일 | 주차 | +|---|---|---| +| ① 문제 정의 | `plan.yaml` 검증 + 사전 등록(커밋) 확인 — 추정 전에 확정 | W3 | +| ② 데이터 수집 | 공공데이터 수집·출처/라이선스 기록 | W2 | +| ③ 지표 구조화 | 패널 구성·품질 점검 | W3 | +| ④ 효과 추정 | DiD/이벤트 스터디/ITS + 반증 → 식별됨·조건부·식별 불가 | W4–5 | +| ⑤ 과잉해석 가드 | 결론 보류 규칙 + 인과 단정 표현 검사 | W6 | +| ⑥ 리포트 | `report.md`·그림·재현 기록 | W7 | + +```bash +make flow CASE=cases/_example_night_clinic # = python -m core.agent <케이스> --allow-uncommitted +``` + +`--allow-uncommitted`는 데모용입니다. 실제 분석은 `plan.yaml`을 먼저 커밋한 뒤 플래그 없이 실행하세요. LLM 서술은 `.env`에 키를 넣고 `--llm`으로 켭니다(`uv pip install -e ".[agent]"`). + ## 라이선스 - **코드**: MIT ([LICENSE](LICENSE)) — © 가짜연구소 Causal Inference Team diff --git a/app/flow_view.py b/app/flow_view.py new file mode 100644 index 0000000..238cbae --- /dev/null +++ b/app/flow_view.py @@ -0,0 +1,277 @@ +"""Flow view: normalize and render core.agent's flow_log.json (schema_version "1"). + +The adapter (`normalize_flow_log`) is the only place that knows the log layout, so a +schema change in core/agent/state.py stays a local fix here. +""" + +from __future__ import annotations + +import json +import os +from pathlib import Path +from typing import Any + +import yaml + +ROOT = Path(__file__).resolve().parents[1] +FLOW_LOG = "flow_log.json" + +# Mirrors core.agent.state.STEPS (copied so the app renders logs even without core). +STEPS: list[tuple[str, str]] = [ + ("define_problem", "① 문제 정의"), + ("collect", "② 데이터 수집"), + ("structure_metrics", "③ 지표 구조화·품질 점검"), + ("estimate", "④ 효과 추정"), + ("guard", "⑤ 과잉해석 가드"), + ("report", "⑥ 리포트·재현 기록"), +] +WEEKS = { # program week each step is taught (README "Flow" table) + "define_problem": "W3", + "collect": "W2", + "structure_metrics": "W3", + "estimate": "W4–5", + "guard": "W6", + "report": "W7", +} +STATUS_BADGE = { + "ok": ("OK", "green"), + "warn": ("WARN", "orange"), + "failed": ("FAILED", "red"), + "needs_human": ("NEEDS HUMAN", "violet"), + "blocked": ("BLOCKED", "red"), + "running": ("RUNNING", "blue"), + "skipped": ("SKIPPED", "gray"), +} +VERDICT_LABEL = { + "identified": ("식별됨", "green"), + "conditional": ("조건부", "orange"), + "not_identified": ("식별 불가", "red"), +} +LLM_KEY_ENVS = ("OPENAI_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_BASE_URL") + + +def cases_dir() -> Path: + """Cases root; PEA_CASES_DIR overrides it (used by tests).""" + return Path(os.environ.get("PEA_CASES_DIR") or ROOT / "cases") + + +def list_cases(root: Path | None = None) -> list[Path]: + root = root or cases_dir() + if not root.is_dir(): + return [] + return sorted(p for p in root.iterdir() if p.is_dir() and (p / "plan.yaml").exists()) + + +def llm_available() -> bool: + return any(os.environ.get(k) for k in LLM_KEY_ENVS) + + +def badge(status: str | None) -> str: + text, color = STATUS_BADGE.get(status or "skipped", (str(status).upper(), "gray")) + return f":{color}-background[{text}]" + + +def verdict_badge(verdict: str | None) -> str: + if not verdict: + return ":gray-background[판정 없음]" + text, color = VERDICT_LABEL.get(verdict, (verdict, "gray")) + return f":{color}-background[**{text}**]" + + +def normalize_flow_log(raw: dict[str, Any]) -> dict[str, Any]: + """Return a log with exactly six steps in order; missing steps become 'skipped'.""" + by_name = {s.get("step"): s for s in raw.get("steps") or [] if isinstance(s, dict)} + steps = [] + for i, (name, title) in enumerate(STEPS, start=1): + s = dict(by_name.get(name) or {}) + s.setdefault("index", i) + s.setdefault("step", name) + s.setdefault("title", title) + s.setdefault("status", "skipped") + s.setdefault("message", "") + s["artifacts"] = s.get("artifacts") or {} + steps.append(s) + return { + "schema_version": str(raw.get("schema_version", "1")), + "case_dir": raw.get("case_dir"), + "question": raw.get("question"), + "status": raw.get("status"), + "verdict": raw.get("verdict"), + "started_at": raw.get("started_at"), + "ended_at": raw.get("ended_at"), + "steps": steps, + "quality": raw.get("quality") or [], + "results": raw.get("results") or [], + "guard": raw.get("guard") or {}, + "report_path": raw.get("report_path"), + } + + +def load_flow_log(case: Path) -> dict[str, Any] | None: + path = case / FLOW_LOG + if not path.exists(): + return None + return normalize_flow_log(json.loads(path.read_text(encoding="utf-8"))) + + +def load_plan_dict(case: Path) -> dict[str, Any]: + try: + data = yaml.safe_load((case / "plan.yaml").read_text(encoding="utf-8")) + except (OSError, yaml.YAMLError): + return {} + return data if isinstance(data, dict) else {} + + +def run_flow_for(case: Path, allow_uncommitted: bool, use_llm: bool) -> dict[str, Any]: + """Run core.agent's flow and return the normalized log.""" + from core.agent import run_flow # imported lazily: the app works without the agent + + state = run_flow(case, allow_uncommitted=allow_uncommitted, use_llm=use_llm) + return normalize_flow_log(state.to_log() if hasattr(state, "to_log") else state) + + +def _resolve(case: Path, rel: str | None) -> Path | None: + if not rel: + return None + p = Path(rel) + if not p.is_absolute(): + p = case / p + return p if p.exists() else None + + +# ---------- rendering ---------- + + +def _plan_summary(st: Any, plan: dict[str, Any], step: dict[str, Any]) -> None: + st.markdown(f"**질문** {plan.get('question', '-')}") + t = plan.get("treatment") or {} + c = plan.get("control") or {} + est = plan.get("estimator") or {} + st.markdown( + f"- 처치: {t.get('definition', '-') if isinstance(t, dict) else t}\n" + f"- 대조: {c.get('definition', '-') if isinstance(c, dict) else c}\n" + f"- 추정: `{est.get('method', '-') if isinstance(est, dict) else est}`\n" + f"- 사전 등록(커밋): {'예' if step['artifacts'].get('committed') else '아니오'}" + ) + + +def _collect(st: Any, plan: dict[str, Any], step: dict[str, Any]) -> None: + a = step["artifacts"] + rows = [ + { + "데이터": s.get("name"), + "제공": s.get("provider"), + "라이선스": s.get("license"), + "URL": s.get("url") or "", + } + for s in plan.get("data_sources") or [] + if isinstance(s, dict) + ] + if rows: + st.dataframe(rows, hide_index=True, width="stretch") + if a.get("licenses"): + st.caption("라이선스: " + ", ".join(map(str, a["licenses"]))) + if a.get("method"): + st.caption(f"수집 방식: {a['method']}") + + +def _quality(st: Any, log: dict[str, Any], step: dict[str, Any]) -> None: + if log["quality"]: + rows = [ + { + "상태": q.get("status"), + "점검": q.get("check"), + "값": str(q.get("value")), + "메시지": q.get("message", ""), + } + for q in log["quality"] + ] + st.dataframe(rows, hide_index=True, width="stretch") + a = step["artifacts"] + if a: + st.caption(" · ".join(f"{k}={v}" for k, v in a.items())) + + +def _fmt(x: Any) -> str: + return f"{x:.3f}" if isinstance(x, int | float) else "-" + + +def _estimate(st: Any, log: dict[str, Any]) -> None: + for r in log["results"]: + st.markdown( + f"**{r.get('method')}** · `{r.get('outcome')}` → {verdict_badge(r.get('verdict'))}" + ) + cols = st.columns(3) + cols[0].metric("추정치", _fmt(r.get("estimate"))) + cols[1].metric("95% CI", f"[{_fmt(r.get('ci_low'))}, {_fmt(r.get('ci_high'))}]") + cols[2].metric("p", _fmt(r.get("p_value"))) + checks = r.get("assumptions_checked") or {} + for name, chk in checks.items(): + if isinstance(chk, dict) and "passed" in chk: + mark = "통과" if chk["passed"] else "실패" + st.caption(f"{name}: {mark} (p={_fmt(chk.get('p_value'))})") + for w in r.get("warnings") or []: + st.warning(w) + + +def _guard(st: Any, log: dict[str, Any]) -> None: + g = log["guard"] + violations = g.get("violations") or [] + if violations: + st.error(f"과잉해석 표현 {len(violations)}건 발견") + for v in violations: + st.markdown(f"- **{v.get('phrase')}** — {v.get('reason')} \n > {v.get('sentence')}") + elif g: + st.success("과잉해석 표현 없음") + if g.get("narrative"): + st.info(g["narrative"]) + st.caption(f"서술 출처: {g.get('narrative_source', '-')}") + + +def _report(st: Any, case: Path, log: dict[str, Any], step: dict[str, Any]) -> None: + a = step["artifacts"] + # report_path may be absolute on the machine that ran the flow; resolve by name instead. + report_name = Path(log["report_path"]).name if log.get("report_path") else None + report = _resolve(case, a.get("report")) or _resolve(case, report_name) + for fig in a.get("figures") or []: + p = _resolve(case, fig) + if p: + st.image(str(p), caption=p.name) + if report: + with st.expander("report.md 미리보기", expanded=False): + st.markdown(report.read_text(encoding="utf-8")) + + +def render_flow(st: Any, case: Path, log: dict[str, Any], plan: dict[str, Any]) -> None: + if plan.get("synthetic_data"): + st.error("## ⚠ SYNTHETIC DATA\n합성 데이터입니다. 결과는 실제 정책 효과가 아닙니다.") + + top = st.columns(3) + top[0].markdown(f"**전체 상태** {badge(log.get('status'))}") + top[1].markdown(f"**판정** {verdict_badge(log.get('verdict'))}") + top[2].caption(f"{log.get('started_at') or '-'} → {log.get('ended_at') or '-'}") + + for step in log["steps"]: + name = step["step"] + dur = step.get("duration_s") + header = f"{step['title']} ({WEEKS.get(name, '')}) · {STATUS_BADGE.get(step['status'], (step['status'],))[0]}" + expanded = step["status"] not in {"ok", "skipped"} or name in {"estimate", "guard"} + with st.expander(header, expanded=expanded): + st.markdown( + f"{badge(step['status'])} {step.get('message', '')}" + + (f" \n:gray[{dur:.2f}s]" if isinstance(dur, int | float) else "") + ) + if step["status"] == "skipped": + continue + if name == "define_problem": + _plan_summary(st, plan, step) + elif name == "collect": + _collect(st, plan, step) + elif name == "structure_metrics": + _quality(st, log, step) + elif name == "estimate": + _estimate(st, log) + elif name == "guard": + _guard(st, log) + elif name == "report": + _report(st, case, log, step) diff --git a/app/pages/1_Flow.py b/app/pages/1_Flow.py new file mode 100644 index 0000000..951dd0f --- /dev/null +++ b/app/pages/1_Flow.py @@ -0,0 +1,61 @@ +"""Flow page: run the 6-step agent flow on a case, or render its existing flow_log.json.""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import streamlit as st + +APP_DIR = Path(__file__).resolve().parents[1] +for p in (APP_DIR, APP_DIR.parent): # app/ for flow_view, repo root for core + if str(p) not in sys.path: + sys.path.insert(0, str(p)) + +import flow_view as fv # noqa: E402 + +st.set_page_config(page_title="Flow · 정책 효과 분석", layout="wide") +st.title("Flow — 6단계 분석 흐름") +st.caption("문제 정의 → 수집 → 지표 구조화 → 추정 → 과잉해석 가드 → 리포트") + +cases = fv.list_cases() +if not cases: + st.info("plan.yaml이 있는 케이스가 없습니다. `cases/_template`을 복사해 시작하세요.") + st.stop() + +case = st.sidebar.selectbox("케이스", cases, format_func=lambda p: p.name) +allow_uncommitted = st.sidebar.checkbox( + "allow uncommitted plan (demo)", + value=False, + help="사전 등록(plan.yaml 커밋) 게이트를 건너뜁니다. 데모 전용.", +) +llm_ok = fv.llm_available() +use_llm = st.sidebar.toggle( + "LLM 서술 사용", + value=False, + disabled=not llm_ok, + help=None if llm_ok else "LLM 키 환경변수(OPENAI_API_KEY 등)가 없어 비활성화됨", +) +run = st.sidebar.button("Run flow", type="primary") + +plan = fv.load_plan_dict(case) +log = None +if run: + with st.spinner("흐름 실행 중..."): + try: + log = fv.run_flow_for(case, allow_uncommitted, use_llm) + except ImportError: + st.error("core.agent를 불러올 수 없습니다. `make install` 후 다시 시도하세요.") + except Exception as exc: # show, don't crash the page + st.exception(exc) +else: + log = fv.load_flow_log(case) + +if log is None: + st.info(f"`{case.name}/flow_log.json`이 없습니다. 사이드바에서 **Run flow**를 누르세요.") + if plan.get("synthetic_data"): + st.warning("이 케이스는 합성 데이터(SYNTHETIC)입니다.") +else: + if not run: + st.caption(f"저장된 `{case.name}/flow_log.json`을 표시합니다 (재실행 안 함).") + fv.render_flow(st, case, log, plan) diff --git a/core/agent/__init__.py b/core/agent/__init__.py index d55d427..99bcf6c 100644 --- a/core/agent/__init__.py +++ b/core/agent/__init__.py @@ -1,31 +1,26 @@ -"""에이전트 툴 인터페이스 (스텁). W4~5 에 Develop 이 LangGraph tool 로 래핑. +"""6단계 에이전트 흐름: 문제 정의 → 수집 → 지표 구조화 → 추정 → 과잉해석 가드 → 리포트. -규약: 각 툴은 JSON 직렬화 가능한 입력/출력만 사용한다. - - validate_plan(path) -> {"ok": bool, "errors": [...]} - - run_estimate(plan_path, data_path) -> EffectResult.to_dict() -LLM 은 plan.yaml 작성까지만 하고, 수치 계산은 반드시 core.estimators 를 호출한다. -""" - -from __future__ import annotations - -import pandas as pd -from pydantic import ValidationError - -from ..schema.plan import load_plan + python -m core.agent cases/_example_night_clinic --allow-uncommitted +- state.py : FlowState + flow_log.json 스키마 +- nodes.py : 6개 노드 (FlowState -> FlowState) +- graph.py : run_flow (순수 Python) / build_graph (LangGraph) +- guard.py : 과잉해석 린터 + 판정별 서술 템플릿 +- llm.py : OpenAI 호환 LLM 호출 (선택; 숫자 계산에는 절대 쓰지 않음) +- tools.py : validate_plan / run_estimate (JSON 툴) +""" -def validate_plan(path: str) -> dict: - try: - load_plan(path) - return {"ok": True, "errors": []} - except ValidationError as e: - return { - "ok": False, - "errors": [f"{'.'.join(map(str, x['loc']))}: {x['msg']}" for x in e.errors()], - } - - -def run_estimate(plan_path: str, data_path: str) -> list[dict]: - from ..pipeline import run_plan # 지연 import - - return [r.to_dict() for r in run_plan(load_plan(plan_path), pd.read_csv(data_path))] +from .graph import build_graph, langgraph_available, run_flow, write_flow_log +from .state import STEPS, FlowState +from .tools import run_estimate, validate_plan + +__all__ = [ + "run_flow", + "build_graph", + "langgraph_available", + "write_flow_log", + "FlowState", + "STEPS", + "validate_plan", + "run_estimate", +] diff --git a/core/agent/__main__.py b/core/agent/__main__.py new file mode 100644 index 0000000..779b046 --- /dev/null +++ b/core/agent/__main__.py @@ -0,0 +1,37 @@ +"""CLI: python -m core.agent [--allow-uncommitted] [--llm] [--question "..."]""" + +from __future__ import annotations + +import argparse +import sys + +from .graph import run_flow + +ICON = {"ok": "OK", "warn": "WARN", "failed": "FAIL", "needs_human": "HUMAN", "blocked": "BLOCK"} + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser( + prog="python -m core.agent", description="6단계 정책효과 분석 흐름 실행" + ) + ap.add_argument("case_dir") + ap.add_argument("--question", help="plan.yaml 이 없을 때 초안 작성에 쓸 분석 질문") + ap.add_argument( + "--allow-uncommitted", action="store_true", help="사전 등록 게이트 우회 (데모 전용)" + ) + ap.add_argument( + "--llm", action="store_true", help="LLM 사용 (OPENAI_BASE_URL/OPENAI_API_KEY/LLM_MODEL)" + ) + ap.add_argument("--engine", choices=["auto", "python", "langgraph"], default="auto") + a = ap.parse_args(argv) + s = run_flow(a.case_dir, a.question, a.allow_uncommitted, a.llm, engine=a.engine) + for st in s.steps: + print( + f"{st.title:<18} {ICON.get(st.status, st.status):<5} {st.duration_s or 0:>6.2f}s {st.message}" + ) + print(f"\n상태={s.status} 판정={s.verdict} 로그={s.case_dir / 'flow_log.json'}") + return {"ok": 0, "warn": 0, "failed": 1}.get(s.status, 2) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/core/agent/graph.py b/core/agent/graph.py new file mode 100644 index 0000000..74e9634 --- /dev/null +++ b/core/agent/graph.py @@ -0,0 +1,85 @@ +"""흐름 실행기: LangGraph StateGraph(있으면) 또는 순수 Python 순차 실행 — 의미는 동일. + + from core.agent import run_flow + state = run_flow("cases/_example_night_clinic", allow_uncommitted=True) + state.to_log() # flow_log.json 과 같은 dict + +두 실행기 모두 '마지막 단계가 ok/warn 일 때만 다음 단계로' 규칙을 따른다 +(failed / needs_human / blocked 이면 즉시 종료). +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import TypedDict + +from .nodes import NODES +from .state import FlowState + + +class GraphState(TypedDict): + flow: FlowState # LangGraph 채널은 하나만 쓰고, 그 안에 FlowState 전체를 담는다 + + +def _init(case_dir, question, allow_uncommitted, use_llm) -> FlowState: + return FlowState( + case_dir=Path(case_dir).resolve(), + question=question, + allow_uncommitted=allow_uncommitted, + use_llm=use_llm, + ) + + +def write_flow_log(s: FlowState) -> Path: + path = s.case_dir / "flow_log.json" + if s.case_dir.exists(): + path.write_text(json.dumps(s.to_log(), ensure_ascii=False, indent=2, default=str), "utf-8") + return path + + +def langgraph_available() -> bool: + try: + import langgraph.graph # noqa: F401 + except ImportError: + return False + return True + + +def build_graph(): + """LangGraph StateGraph: ①→②→…→⑥, 각 단계 뒤 조건부 엣지로 중단 여부 결정.""" + from langgraph.graph import END, START, StateGraph + + g = StateGraph(GraphState) + for fn in NODES: + g.add_node(fn.__name__, lambda d, fn=fn: {"flow": fn(d["flow"])}) + g.add_edge(START, NODES[0].__name__) + names = [fn.__name__ for fn in NODES] + for a, b in zip(names, names[1:], strict=False): + g.add_conditional_edges(a, lambda d, b=b: b if d["flow"].can_continue else END, [b, END]) + g.add_edge(NODES[-1].__name__, END) + return g.compile() + + +def run_flow( + case_dir, + question: str | None = None, + allow_uncommitted: bool = False, + use_llm: bool = False, + engine: str = "python", + write_log: bool = True, +) -> FlowState: + """engine: "python"(기본, 의존성 없음) | "langgraph" | "auto"(설치돼 있으면 langgraph).""" + s = _init(case_dir, question, allow_uncommitted, use_llm) + if engine == "auto": + engine = "langgraph" if langgraph_available() else "python" + if engine == "langgraph": + s = build_graph().invoke({"flow": s})["flow"] + else: + for node in NODES: + s = node(s) + if not s.can_continue: + break + if write_log: + write_flow_log(s) + return s diff --git a/core/agent/guard.py b/core/agent/guard.py new file mode 100644 index 0000000..dec2656 --- /dev/null +++ b/core/agent/guard.py @@ -0,0 +1,98 @@ +"""⑤ 과잉해석 가드: 판정에 맞는 서술 템플릿 + 인과 단정 표현 린터. + +규칙 + 1) 판정(verdict)이 identified 가 아니면 인과·확정 표현(CAUSAL) 금지 + 2) 신뢰구간이 0을 포함하면 '효과 없음' 류 금지 → '판별 불가'로 써야 함 +린터는 단순 문자열/정규식 매칭이다. 부정문("입증되지 않았다")도 잡히는데, 의도된 보수성이다. +""" + +from __future__ import annotations + +import re + +from ..estimators.result import EffectResult + +CAUSAL = [ + "효과가 입증", + "입증", + "증명", + "때문에", + "덕분에", + "로 인해", + "인과적으로 확인", + "확실히", + "명백히", + r"\bcaus(e|es|ed|ing)\b", + r"\bproves?\b", + r"\bproven\b", + r"\bdue to\b", + r"\bbecause of\b", + r"\bdemonstrates?\b", + r"\bdefinitely\b", +] +NULL_EFFECT = [ + "효과 없음", + "효과가 없", + "효과는 없", + r"\bno effect\b", + r"\bhad no (significant )?effect\b", +] + +TRIGGER_KO = { + "pretrend_rejected": "사전추세 차이(평행추세 가정 기각)", + "few_clusters": "클러스터 수 부족(표준오차 과소추정 위험)", + "staggered_adoption": "시차 도입에서 TWFE 편향 위험", + "placebo_significant": "가짜 도입시점 검정 유의", + "ci_crosses_zero": "신뢰구간이 0을 포함", + "short_pre_period": "사전 기간 부족", +} + + +def _sentences(text: str) -> list[str]: + return [s.strip() for s in re.split(r"(?<=[.!?。])\s+|\n+", text) if s.strip()] + + +def lint(text: str, verdict: str, ci_crosses_zero: bool) -> list[dict]: + """금지 표현 목록을 반환. 빈 리스트면 통과.""" + rules = [] + if verdict != "identified": + rules += [(p, f"판정이 '{verdict}' 인데 인과·확정 표현 사용") for p in CAUSAL] + if ci_crosses_zero: + rules += [ + (p, "신뢰구간이 0을 포함 → '효과 없음'이 아니라 '판별 불가'로 서술") + for p in NULL_EFFECT + ] + out = [] + for sent in _sentences(text): + for pat, reason in rules: + m = re.search(pat, sent, flags=re.IGNORECASE) + if m: + out.append({"phrase": m.group(0), "sentence": sent, "reason": reason}) + return out + + +def template_narrative(r: EffectResult, unit: str | None = None, name: str | None = None) -> str: + """판정별 결정론적 서술 (LLM 미사용 시 기본값). 린터를 항상 통과하도록 작성.""" + u = f" {unit}" if unit else "" + ci = f"95% CI {r.ci_low:.2f} ~ {r.ci_high:.2f}" + why = ", ".join(TRIGGER_KO.get(t, t) for t in r.triggers) or "없음" + if r.verdict == "identified": + return ( + f"**요약**: 정책 도입 후 처치집단의 {name or r.outcome}은(는) 통제집단 대비 평균 " + f"{r.estimate:+.2f}{u} 변화한 것으로 추정됩니다 ({ci}). 이 해석은 plan.yaml 의 식별 가정이 " + "성립할 때에만 유효하며, 자동 점검에서 가정이 반박되지 않았다는 뜻이지 검증되었다는 뜻은 아닙니다." + ) + if r.verdict == "conditional": + zero = ( + " 신뢰구간이 0을 포함하므로 효과의 방향과 크기는 판별 불가입니다." + if ("ci_crosses_zero" in r.triggers) + else "" + ) + return ( + f"**요약(조건부)**: 추정치는 {r.estimate:+.2f}{u} ({ci}) 이지만 다음 위험 신호가 있어 " + f"조건부로만 보고합니다: {why}.{zero} 결론 전에 보완 분석이 필요합니다." + ) + return ( + f"**요약(식별 불가)**: 다음 사유로 이 설계에서는 정책 효과를 추정치로 보고하지 않습니다: {why}. " + f"표의 수치({r.estimate:+.2f}{u})는 진단용이며 정책 효과로 해석하면 안 됩니다." + ) diff --git a/core/agent/llm.py b/core/agent/llm.py new file mode 100644 index 0000000..891aed6 --- /dev/null +++ b/core/agent/llm.py @@ -0,0 +1,46 @@ +"""LLM 호출 (선택). OpenAI 호환 Chat Completions 엔드포인트 하나로 통일. + +환경변수 (.env.example 참고): + OPENAI_BASE_URL 기본 https://api.openai.com/v1 (Ollama: http://localhost:11434/v1, vLLM 등) + OPENAI_API_KEY 로컬 오픈 LLM 이면 비워도 됨 + LLM_MODEL 예: gpt-4o-mini, qwen2.5:7b-instruct +규칙: LLM 은 '글(계획 초안·서술)'만 쓴다. 숫자 계산은 항상 core.estimators. +""" + +from __future__ import annotations + +import os + +import requests + + +def configured() -> bool: + return bool(os.getenv("LLM_MODEL")) and bool( + os.getenv("OPENAI_API_KEY") or os.getenv("OPENAI_BASE_URL") + ) + + +def chat(system: str, user: str, temperature: float = 0.2, timeout: int = 120) -> str: + base = os.getenv("OPENAI_BASE_URL") or "https://api.openai.com/v1" + key = os.getenv("OPENAI_API_KEY", "") + r = requests.post( + f"{base.rstrip('/')}/chat/completions", + headers={"Authorization": f"Bearer {key}"} if key else {}, + json={ + "model": os.environ["LLM_MODEL"], + "temperature": temperature, + "messages": [{"role": "system", "content": system}, {"role": "user", "content": user}], + }, + timeout=timeout, + ) + r.raise_for_status() + return r.json()["choices"][0]["message"]["content"].strip() + + +def strip_fence(text: str) -> str: + """```yaml ... ``` 코드펜스 제거.""" + t = text.strip() + if t.startswith("```"): + t = t.split("\n", 1)[1] if "\n" in t else "" + t = t.rsplit("```", 1)[0] + return t.strip() diff --git a/core/agent/nodes.py b/core/agent/nodes.py new file mode 100644 index 0000000..005573e --- /dev/null +++ b/core/agent/nodes.py @@ -0,0 +1,373 @@ +"""6단계 노드. 각 노드는 순수 함수 `FlowState -> FlowState` (입력 상태를 바꾸지 않고 복사본 반환). + + ① define_problem → ② collect → ③ structure_metrics → ④ estimate → ⑤ guard → ⑥ report + +노드 본문은 `(status, message, artifacts)` 만 돌려주고, 로그·시간·예외 처리는 @step 이 맡는다. +멘티가 단계를 추가할 때도 같은 패턴을 쓰면 된다. +""" + +from __future__ import annotations + +import hashlib +import json +import platform +import subprocess +import sys +import time +from functools import wraps +from importlib import metadata +from pathlib import Path + +import pandas as pd +import yaml +from pydantic import ValidationError + +from ..adapters import REGISTRY, LocalFileAdapter, SourceMeta +from ..pipeline import run_plan +from ..report import event_study_plot, its_plot, raw_trends, render_report +from ..schema.plan import Plan, load_plan +from . import guard as G +from . import llm +from .state import STEPS, FlowState, StepLog, now + +ROOT = Path(__file__).resolve().parents[2] +TEMPLATE_PLAN = ROOT / "cases" / "_template" / "plan.yaml" +_TITLES = dict(STEPS) + + +class FlowError(Exception): + """사용자에게 그대로 보여줄 한국어 오류.""" + + +def step(fn): + name = fn.__name__ + + @wraps(fn) + def node(state: FlowState) -> FlowState: + s = state.model_copy(deep=True) + log = StepLog(index=len(s.steps) + 1, step=name, title=_TITLES[name]) + t0 = time.perf_counter() + try: + log.status, log.message, log.artifacts = fn(s) + except FlowError as e: + log.status, log.message = "failed", str(e) + except Exception as e: # noqa: BLE001 — 흐름을 멈추고 로그에 남긴다 + log.status, log.message = "failed", f"{type(e).__name__}: {e}" + log.ended_at, log.duration_s = now(), round(time.perf_counter() - t0, 3) + s.steps.append(log) + return s + + return node + + +def _sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def _git(cwd: Path, *args: str) -> subprocess.CompletedProcess: + return subprocess.run(["git", *args], cwd=cwd, capture_output=True, text=True) + + +def plan_committed(case_dir: Path) -> bool: + """plan.yaml 이 git 에 추적되고 HEAD 대비 수정이 없으면 True (사전 등록 확인).""" + tracked = _git(case_dir, "ls-files", "--error-unmatch", "plan.yaml").returncode == 0 + return tracked and _git(case_dir, "diff", "--quiet", "HEAD", "--", "plan.yaml").returncode == 0 + + +# ① ------------------------------------------------------------------------------------------ +@step +def define_problem(s: FlowState): + path = s.case_dir / "plan.yaml" + if not path.exists(): + s.case_dir.mkdir(parents=True, exist_ok=True) + template = TEMPLATE_PLAN.read_text(encoding="utf-8") + if s.use_llm and llm.configured() and s.question: + text = llm.strip_fence( + llm.chat( + "너는 정책 효과 분석 설계자다. 주어진 템플릿과 같은 필드만 사용해 plan.yaml 을 작성하라. " + "YAML 만 출력하라. 데이터를 보기 전 사전 등록 문서이므로 보류 조건(abstention)을 보수적으로 둔다.", + f"분석 질문: {s.question}\n\n템플릿:\n{template}", + ) + ) + path.write_text(text, encoding="utf-8") + try: + Plan.model_validate(yaml.safe_load(text)) + msg = "LLM 이 plan.yaml 초안을 작성했습니다. 사람이 검토·수정 후 커밋하고 다시 실행하세요." + except (ValidationError, yaml.YAMLError) as e: + msg = f"LLM 초안이 스키마 검증에 실패했습니다 — 직접 수정하세요:\n{e}" + return "needs_human", msg, {"plan": "plan.yaml", "drafted_by": "llm"} + header = f"# 분석 질문(입력): {s.question}\n" if s.question else "" + path.write_text(header + template, encoding="utf-8") + return ( + "needs_human", + "plan.yaml 이 없어 템플릿을 복사했습니다(LLM 미설정). 작성·커밋 후 다시 실행하세요.", + {"plan": "plan.yaml", "drafted_by": "template"}, + ) + + try: + s.plan = load_plan(path) + except ValidationError as e: + errs = "; ".join(f"{'.'.join(map(str, x['loc']))}: {x['msg']}" for x in e.errors()) + raise FlowError(f"plan.yaml 스키마 오류 — {errs}") from e + arts = { + "plan": "plan.yaml", + "plan_sha256": _sha256(path), + "committed": plan_committed(s.case_dir), + } + if arts["committed"]: + return "ok", f"분석계획 검증 완료, 사전 등록(git 커밋) 확인: {s.plan.case_id}", arts + if s.allow_uncommitted: + return ( + "warn", + "⚠️ plan.yaml 이 커밋되지 않았습니다(--allow-uncommitted, 데모 전용). " + "실제 분석에서는 결과를 보기 전에 계획을 커밋해야 합니다.", + arts, + ) + return ( + "blocked", + "사전 등록 게이트: plan.yaml 을 먼저 git 에 커밋하세요 " + "(데이터를 본 뒤 계획을 바꾸는 것을 막기 위함). 데모는 --allow-uncommitted.", + arts, + ) + + +# ② ------------------------------------------------------------------------------------------ +@step +def collect(s: FlowState): + ds = s.plan.data_sources[0] + path = s.case_dir / ds.path + if path.exists(): + how = "기존 스냅샷 재사용 (새로 받으려면 파일 삭제 후 재실행)" + elif (s.case_dir / "fetch.py").exists(): + r = subprocess.run( + [sys.executable, "fetch.py"], cwd=s.case_dir, capture_output=True, text=True + ) + if r.returncode != 0: + raise FlowError(f"fetch.py 실행 실패:\n{r.stderr[-800:]}") + how = "fetch.py 실행" + elif ds.adapter == "local": + meta = SourceMeta(ds.name, ds.provider, ds.license, ds.url) + ad = LocalFileAdapter(s.case_dir / ds.query["path"], meta) + ad.save(ad.fetch(), path, ds.query) + how = "LocalFileAdapter" + elif ds.adapter in REGISTRY: + ad = REGISTRY[ds.adapter]() + ad.save(ad.fetch(**ds.query), path, ds.query) + how = f"{ds.adapter} 어댑터" + else: + raise FlowError( + f"데이터가 없습니다: {ds.path} 가 없고 fetch.py 나 알려진 어댑터({list(REGISTRY)})도 없습니다." + ) + if not path.exists(): + raise FlowError(f"수집 후에도 {ds.path} 가 생성되지 않았습니다.") + s.data_path = path + licenses = sorted({d.license for d in s.plan.data_sources}) + arts = {"data": ds.path, "data_sha256": _sha256(path), "licenses": licenses, "method": how} + src = path.with_suffix(".source.json") + if src.exists(): + arts["source_meta"] = str(src.relative_to(s.case_dir)) + notes = [] + if s.plan.synthetic_data: + notes.append("⚠️ 합성 데이터 — 결과는 실제 정책 효과가 아님") + if {"KOGL-3", "KOGL-4"} & set(licenses): + notes.append("공공누리 3·4유형(변경금지) 포함 — 가공 데이터 재배포 불가") + return ("warn" if notes else "ok"), " / ".join([how, *notes]), arts + + +# ③ ------------------------------------------------------------------------------------------ +def quality_checks(p: Plan, df: pd.DataFrame) -> list[dict]: + """(점검, 값, 상태, 메시지) 목록. status: ok | warn | failed.""" + t, u, g = p.time.col, p.unit.id_col, p.treatment.group_col + outs = [o.col for o in p.outcomes] + cols = [u, t, g, p.treatment.first_treat_col, p.estimator.cluster_col, *outs] + need = [t, *outs] if p.estimator.method == "its" else [c for c in cols if c] + need = list(dict.fromkeys(need + p.estimator.covariates)) + q = [] + + def add(check, value, fail=False, warn=False, msg=""): + status = "failed" if fail else "warn" if warn else "ok" + q.append({"check": check, "value": value, "status": status, "message": msg}) + + missing = [c for c in need if c not in df.columns] + add("필수 컬럼", len(need) - len(missing), bool(missing), msg=f"없는 컬럼: {missing}") + if missing: + return q + ints = df[t].dropna() + ok = pd.api.types.is_integer_dtype(ints) or bool(ints.mod(1).eq(0).all()) + add("시간 컬럼 정수형", str(df[t].dtype), not ok, msg=f"'{t}' 는 정수(연도/0,1,2…)여야 함") + for c in need: + rate, key = float(df[c].isna().mean()), c in (u, t, g) + add(f"결측률 {c}", round(rate, 4), (key and rate > 0) or rate > 0.2, rate > 0, + msg=f"결측 {rate:.1%}" + (" — 식별 컬럼은 결측 불가" if key else "")) # fmt: skip + if (tt := p.treatment.treat_time) is not None: + periods = df[t].dropna().unique() + n_pre, n_post = int((periods < tt).sum()), int((periods >= tt).sum()) + min_pre = p.thresholds.min_pre_periods + add("사전 기간 수", n_pre, n_pre == 0, n_pre < min_pre, f"최소 {min_pre}기 권장") + add("사후 기간 수", n_post, n_post == 0, msg="사후 기간이 없습니다") + if p.estimator.method != "its": + by = df.groupby(u)[g].max() + n_t, n_c, dup = int((by == 1).sum()), int((by == 0).sum()), int(df.duplicated([u, t]).sum()) + add("처치 단위 수", n_t, n_t == 0, msg="처치 단위가 없습니다") + add("통제 단위 수", n_c, n_c == 0, msg="통제 단위가 없습니다") + add("단위×시점 중복", dup, dup > 0, msg="한 단위·시점에 행이 여러 개") + for x in q: # 통과한 항목은 메시지 비움 + x["message"] = x["message"] if x["status"] != "ok" else "" + return q + + +@step +def structure_metrics(s: FlowState): + df = pd.read_csv(s.data_path) + s.quality = q = quality_checks(s.plan, df) + if failed := [x for x in q if x["status"] == "failed"]: + detail = "; ".join(f"{x['check']}: {x['message']}" for x in failed) + raise FlowError(f"데이터 품질 점검 실패 — {detail}") + warns = [x["check"] for x in q if x["status"] == "warn"] + msg = f"{len(q)}개 점검" + (f", 주의: {warns}" if warns else " 모두 통과") + return ( + ("warn" if warns else "ok"), + msg, + {"rows": len(df), "units": int(df[s.plan.unit.id_col].nunique())}, + ) + + +# ④ ------------------------------------------------------------------------------------------ +@step +def estimate(s: FlowState): + s.results = run_plan(s.plan, pd.read_csv(s.data_path)) # 숫자는 여기서만 계산 (LLM 관여 없음) + arts = { + r.outcome: { + "estimate": round(r.estimate, 4), + "ci": [round(r.ci_low, 4), round(r.ci_high, 4)], + "verdict": r.verdict, + } + for r in s.results + } + r = s.results[0] + msg = f"{r.method}: {r.estimate:+.3f} [{r.ci_low:.3f}, {r.ci_high:.3f}] → {r.verdict}" + return ("warn" if any(x.warnings for x in s.results) else "ok"), msg, arts + + +# ⑤ ------------------------------------------------------------------------------------------ +@step +def guard(s: FlowState): + r = next(x for x in s.results if x.outcome == s.plan.primary_outcome.col) + zero = r.ci_low <= 0 <= r.ci_high + o = s.plan.primary_outcome + text, source, rejected = G.template_narrative(r, o.unit, o.name), "template", [] + if s.use_llm and llm.configured(): + facts = {k: v for k, v in r.to_dict().items() if k != "extra"} + draft = llm.chat( + "너는 정책 효과 분석 결과를 3문장 이내 한국어로 요약한다. 주어진 수치만 쓰고 새 숫자를 만들지 마라. " + "판정이 identified 가 아니면 인과·확정 표현(입증, 때문에, 덕분에 등)을 쓰지 마라. " + "신뢰구간이 0을 포함하면 '효과 없음' 대신 '판별 불가'라고 써라.", + json.dumps(facts, ensure_ascii=False, default=str), + ) + rejected = G.lint(draft, r.verdict, zero) + if not rejected: + text, source = draft, "llm" + violations = G.lint(text, r.verdict, zero) + s.guard = { + "verdict": r.verdict, + "narrative_source": source, + "passed": not violations, + "violations": violations, + "rejected_llm_violations": rejected, + "narrative": text, + } + if violations: + raise FlowError(f"서술 린트 실패: {violations}") + msg = f"판정 {r.verdict}, 서술={source}" + if rejected: + msg += f" (LLM 서술에서 과잉해석 {len(rejected)}건 → 템플릿으로 대체)" + return ( + ("ok" if r.verdict == "identified" and not rejected else "warn"), + msg, + {"triggers": r.triggers}, + ) + + +# ⑥ ------------------------------------------------------------------------------------------ +def _checks(plan, r) -> dict: + out, ac = {}, r.assumptions_checked + for a in plan.assumptions: + if a.check == "pretrend_test" and "parallel_pretrends" in ac: + x = ac["parallel_pretrends"] + out[a.name] = ( + f"사전계수 결합 Wald p={x['p_value']:.3f} → {'통과' if x['passed'] else '기각'}" + ) + elif a.check == "placebo_time" and "placebo_time" in ac: + x = ac["placebo_time"] + out[a.name] = ( + f"가짜 도입({x['fake_treat_time']}) 추정 {x['estimate']:.3f}, " + f"p={x['p_value']:.3f} → {'통과' if x['passed'] else '실패'}" + ) + return out + + +def _versions() -> dict: + pk = "pandas numpy scipy pyfixest statsmodels pydantic matplotlib langgraph".split() + out = {"python": platform.python_version()} + for p in pk: + try: + out[p] = metadata.version(p) + except metadata.PackageNotFoundError: + pass + return out + + +@step +def report(s: FlowState): + p, df = s.plan, pd.read_csv(s.data_path) + r = next(x for x in s.results if x.outcome == p.primary_outcome.col) + figdir = s.case_dir / "figures" + figdir.mkdir(exist_ok=True) + figs = {} + if p.estimator.method == "its": + its_plot( + df, + r.outcome, + p.time.col, + p.treatment.treat_time, + r.extra["fitted"], + r.extra["counterfactual"], + figdir / "its.png", + ) + figs["ITS"] = "figures/its.png" + else: + tt = p.treatment.treat_time or df[p.treatment.first_treat_col].min() + raw_trends(df, r.outcome, p.time.col, p.treatment.group_col, tt, figdir / "raw_trends.png") + figs["Raw trends"] = "figures/raw_trends.png" + if "coefs" in r.extra: + event_study_plot(r.extra["coefs"], figdir / "event_study.png") + figs["Event study"] = "figures/event_study.png" + md = render_report(p, s.results, figs, _checks(p, r), narrative=s.guard.get("narrative")) + s.report_path = s.case_dir / "report.md" + s.report_path.write_text(md, encoding="utf-8") + + sha = _git(s.case_dir, "rev-parse", "HEAD") + dirty = _git(s.case_dir, "status", "--porcelain", "--", ".") + manifest = { + "case_id": p.case_id, + "started_at": s.started_at, + "finished_at": now(), + "git_sha": sha.stdout.strip() if sha.returncode == 0 else None, + "git_dirty": bool(dirty.stdout.strip()) if dirty.returncode == 0 else None, + "plan_sha256": _sha256(s.case_dir / "plan.yaml"), + "data_sha256": _sha256(s.data_path), + "data_path": str(s.data_path.relative_to(s.case_dir)), + "versions": _versions(), + "platform": platform.platform(), + "verdicts": {x.outcome: x.verdict for x in s.results}, + } + (s.case_dir / "run_manifest.json").write_text( + json.dumps(manifest, ensure_ascii=False, indent=2), "utf-8" + ) + arts = {"report": "report.md", "manifest": "run_manifest.json", "figures": list(figs.values())} + return "ok", f"리포트 {len(figs)}개 그림 포함 작성", arts + + +NODES = [define_problem, collect, structure_metrics, estimate, guard, report] + +__all__ = ["NODES", "FlowError", "plan_committed"] diff --git a/core/agent/state.py b/core/agent/state.py new file mode 100644 index 0000000..311d5d3 --- /dev/null +++ b/core/agent/state.py @@ -0,0 +1,135 @@ +"""에이전트 흐름 상태(FlowState)와 flow_log.json 스키마. + +flow_log.json (schema_version "1") — Streamlit Flow 페이지·CI 가 읽는 계약: +{ + "schema_version": "1", + "case_dir": "cases/_example_night_clinic", + "question": str | null, + "status": "ok" | "warn" | "failed" | "needs_human" | "blocked" | "running", + "verdict": "identified" | "conditional" | "not_identified" | null, # 가장 보수적인 판정 + "started_at": ISO8601, "ended_at": ISO8601 | null, + "steps": [ # 실행된 단계만 순서대로 (중단 시 이후 단계 없음) + {"index": 1, "step": "define_problem", "title": "① 문제 정의", + "status": "ok"|"warn"|"failed"|"needs_human"|"blocked", + "started_at": ISO8601, "ended_at": ISO8601, "duration_s": float, + "message": str (한국어), "artifacts": {name: 경로(케이스 기준) 또는 값}} + ], + "quality": [{"check": str, "value": any, "status": "ok"|"warn"|"failed", "message": str}], + "results": [EffectResult.to_dict()], # 대용량 배열(extra.fitted 등)은 제외 + "guard": {"verdict": str, "narrative_source": "template"|"llm", "passed": bool, + "violations": [{"phrase": str, "sentence": str, "reason": str}], "narrative": str}, + "report_path": str | null +} +run_flow() 는 FlowState 를 반환하고, FlowState.to_log() 가 위 dict 를 만든다. +""" + +from __future__ import annotations + +from datetime import UTC, datetime +from pathlib import Path +from typing import Any, Literal + +from pydantic import BaseModel, ConfigDict, Field + +from ..estimators.result import EffectResult +from ..schema.plan import Plan + +Status = Literal["ok", "warn", "failed", "needs_human", "blocked", "running"] +CONTINUE = {"ok", "warn"} # 이 상태일 때만 다음 단계로 진행 +STEPS = [ + ("define_problem", "① 문제 정의"), + ("collect", "② 데이터 수집"), + ("structure_metrics", "③ 지표 구조화·품질 점검"), + ("estimate", "④ 효과 추정"), + ("guard", "⑤ 과잉해석 가드"), + ("report", "⑥ 리포트·재현 기록"), +] +_ORDER = ["ok", "warn", "needs_human", "blocked", "failed"] + + +def now() -> str: + return datetime.now(UTC).isoformat(timespec="seconds") + + +class StepLog(BaseModel): + index: int + step: str + title: str + status: Status = "running" + started_at: str = Field(default_factory=now) + ended_at: str | None = None + duration_s: float | None = None + message: str = "" + artifacts: dict[str, Any] = {} + + +class FlowState(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) + + case_dir: Path + question: str | None = None + allow_uncommitted: bool = False + use_llm: bool = False + started_at: str = Field(default_factory=now) + plan: Plan | None = None + data_path: Path | None = None + quality: list[dict[str, Any]] = [] + results: list[EffectResult] = [] + guard: dict[str, Any] = {} + report_path: Path | None = None + steps: list[StepLog] = [] + + @property + def last_status(self) -> str: + return self.steps[-1].status if self.steps else "running" + + @property + def can_continue(self) -> bool: + return not self.steps or self.last_status in CONTINUE + + @property + def status(self) -> str: + if not self.steps: + return "running" + worst = max((s.status for s in self.steps), key=_ORDER.index) + done = len(self.steps) == len(STEPS) or worst not in CONTINUE + return worst if done else "running" + + @property + def verdict(self) -> str | None: + v = [r.verdict for r in self.results] + order = ["identified", "conditional", "not_identified"] + return max(v, key=order.index) if v else None + + def to_log(self) -> dict: + def _res(r: EffectResult) -> dict: + d = r.to_dict() + d["extra"] = { + k: v for k, v in d["extra"].items() if k not in {"fitted", "counterfactual"} + } + return d + + def _rel(p: Path | None) -> str | None: + # 공개 레포에 실행자의 로컬 경로가 남지 않도록 케이스 폴더 기준 상대경로만 기록 + if p is None: + return None + p = Path(p) + try: + return str(p.resolve().relative_to(Path(self.case_dir).resolve())) + except ValueError: + return p.name + + return { + "schema_version": "1", + "case_dir": f"cases/{Path(self.case_dir).name}", + "question": self.question or (self.plan.question if self.plan else None), + "status": self.status, + "verdict": self.verdict, + "started_at": self.started_at, + "ended_at": self.steps[-1].ended_at if self.steps else None, + "steps": [s.model_dump() for s in self.steps], + "quality": self.quality, + "results": [_res(r) for r in self.results], + "guard": self.guard, + "report_path": _rel(self.report_path), + } diff --git a/core/agent/tools.py b/core/agent/tools.py new file mode 100644 index 0000000..f68d301 --- /dev/null +++ b/core/agent/tools.py @@ -0,0 +1,25 @@ +"""에이전트 툴 (JSON 입출력). LangGraph/MCP tool 로 그대로 래핑할 수 있는 얇은 함수들.""" + +from __future__ import annotations + +import pandas as pd +from pydantic import ValidationError + +from ..schema.plan import load_plan + + +def validate_plan(path: str) -> dict: + try: + load_plan(path) + return {"ok": True, "errors": []} + except ValidationError as e: + return { + "ok": False, + "errors": [f"{'.'.join(map(str, x['loc']))}: {x['msg']}" for x in e.errors()], + } + + +def run_estimate(plan_path: str, data_path: str) -> list[dict]: + from ..pipeline import run_plan # 지연 import + + return [r.to_dict() for r in run_plan(load_plan(plan_path), pd.read_csv(data_path))] diff --git a/core/report/markdown.py b/core/report/markdown.py index ff6139b..b27df63 100644 --- a/core/report/markdown.py +++ b/core/report/markdown.py @@ -32,7 +32,11 @@ def results_table(results: list[EffectResult]) -> str: def render_report( - plan, results: list[EffectResult], figures: dict[str, str], checks: dict | None = None + plan, + results: list[EffectResult], + figures: dict[str, str], + checks: dict | None = None, + narrative: str | None = None, ) -> str: worst = max( results, key=lambda r: ["identified", "conditional", "not_identified"].index(r.verdict) @@ -49,6 +53,7 @@ def render_report( "", f"**종합 판정**: {VERDICT_KO[worst.verdict]}", "", + *([narrative, ""] if narrative else []), "## 1. 설계", "", f"- 분석 단위: {plan.unit.name} (`{plan.unit.id_col}`), 기간 {plan.time.start}–{plan.time.end} ({plan.time.freq})", diff --git a/core/schema/plan.py b/core/schema/plan.py index 4e664c8..058d3a3 100644 --- a/core/schema/plan.py +++ b/core/schema/plan.py @@ -120,6 +120,8 @@ class DataSource(_Base): url: str | None = None license: License adapter: str | None = Field(None, description="core.adapters 의 어댑터 이름") + path: str = Field("data/panel.csv", description="케이스 폴더 기준 분석용 스냅샷 경로") + query: dict = Field({}, description="어댑터 fetch() 인자 (fetch.py 가 없을 때 사용)") notes: str = "" diff --git a/docs/ops/group-guide.md b/docs/ops/group-guide.md new file mode 100644 index 0000000..8e0b999 --- /dev/null +++ b/docs/ops/group-guide.md @@ -0,0 +1,57 @@ +# 조별 운영 가이드 + +조(약 5명)마다 공공데이터로 정책 효과 질문을 **하나** 정하고, 공용 저장소의 `cases/<조-주제>/` 폴더 하나에 결과를 쌓습니다. +전체 흐름은 6단계 에이전트 Flow(README의 Flow 절)와 같고, 조원 역할도 이 단계에 맞춰 나눕니다. + +- 완성 예시: [`cases/_example_night_clinic/`](../../cases/_example_night_clinic/) — 합성 데이터로 6단계를 끝까지 돌린 샘플 +- 주제 후보: [`docs/strategy/topic-guide.md`](../strategy/topic-guide.md) (T1~T8) +- 분석계획 작성법: [`docs/strategy/plan-guide.md`](../strategy/plan-guide.md) + +## 1. 역할 (5인 기준, 겸임 가능) + +| 역할 | 맡는 단계 | 주 산출물 | +|---|---|---| +| **문제 정의** (조장 겸임 권장) | ① 문제 정의 | `plan.yaml` — 질문·처치·대조·결과지표·가정·보류 규칙 | +| **데이터 수집** | ② 수집 | `fetch.py`, `data/README.md` (출처·라이선스·수집일) | +| **지표·품질** | ③ 지표 구조화 | 정제 패널(`data/panel.csv`), 품질 점검 통과 | +| **추정** | ④ 효과 추정 | `estimate.py`, 사전추세·placebo 결과 | +| **리포트·에이전트** | ⑤ 과잉해석 방지, ⑥ 리포트 | `report.md`, 그림, `flow_log.json` | + +4명 이하면 **수집 + 지표·품질**, **추정 + 리포트**를 한 사람이 맡습니다. 역할은 주마다 바꿔도 됩니다. + +## 2. 주차별 마일스톤 + +| 모임 | 주차 | 조가 끝낼 것 | 인증(커밋/PR) | +|---|---|---|---| +| 9.27(일) | W2 | 조 편성, 주제 후보 2개 선택, 데이터 접근 확인(API 키 발급) | `cases/<조>/` 폴더 + 이슈 "케이스 제안" | +| 10.2(금) 오프라인 | — | 주제 1개 확정, **`plan.yaml` 사전 등록 PR** | plan.yaml PR 병합 | +| 10.4(일) | W3 | `fetch.py` 동작, ③ 품질 점검 통과 | 수집·정제 PR | +| 10.11(일) | W4 | ④ 1차 추정 + 사전추세 그림 | 추정 PR | +| 10.18(일) | W5 | 반박 검정 추가, 에이전트 Flow로 ①~⑥ 한 번에 실행 | `flow_log.json` 커밋 | +| 10.25(일) | W6 | ⑤ 가드 통과(과잉 인과 표현 제거), 판정 문구 확정 | 리포트 PR | +| 11.1(일) | W7 | 다른 조가 README만 보고 재현 성공 → 공개 | 교차 재현 결과 이슈 | + +Level 1(정리된 데이터 + 문서)에서 멈춰도 산출물입니다. 판정이 "식별 불가"로 나와도 근거가 명확하면 좋은 결과입니다. + +## 3. 브랜치·PR 규칙 + +- 브랜치: `group/<짧은-설명>` (예: `group2/fetch-airkorea`). `main`에 직접 push 하지 않습니다. +- 수정 범위: **자기 조 `cases/<조>/` 폴더만.** `core/` 수정이 필요하면 이슈를 먼저 엽니다. +- **사전 등록**: `plan.yaml`을 먼저 PR로 병합한 뒤 데이터를 봅니다. Flow ①단계가 커밋되지 않은 plan.yaml을 막습니다. +- PR 하나에 한 단계. 리뷰 1명 승인 + CI 통과 후 병합(Squash). +- 올리면 안 되는 것: API 키(`.env`), 원자료 대용량 파일(`data/raw/`), 개인정보가 담긴 데이터, 재배포가 금지된 데이터(라이선스 확인). + +## 4. 실행 방법 + +```bash +make install # 최초 1회 +cp -r cases/_template cases/group1-topic # 조 폴더 만들기 +make flow CASE=cases/group1-topic # ①~⑥ 실행 (plan.yaml 커밋 후) +make app # Streamlit → Flow 페이지에서 단계별 결과 확인 +``` + +## 5. 활동 인증 + +- 매 모임 전까지 조 브랜치에 커밋 1회 이상 + PR 링크를 디스코드 조 채널에 공유합니다. +- 멘토는 `python scripts/weekly_activity.py`로 조별 커밋·변경 파일을 확인합니다 ([monitoring.md](monitoring.md)). +- 조모임은 전체 모임과 별도로 자유롭게 잡고, 결정 사항은 PR 설명이나 이슈에 남깁니다. diff --git a/tests/core/test_agent.py b/tests/core/test_agent.py new file mode 100644 index 0000000..e767794 --- /dev/null +++ b/tests/core/test_agent.py @@ -0,0 +1,146 @@ +import json +import shutil +import subprocess +from pathlib import Path + +import pytest + +from core.agent import STEPS, FlowState, build_graph, langgraph_available, run_flow +from core.agent.guard import lint, template_narrative +from core.agent.nodes import define_problem +from core.estimators.result import EffectResult + +ROOT = Path(__file__).resolve().parents[2] +EXAMPLE = ROOT / "cases" / "_example_night_clinic" + + +@pytest.fixture +def case(tmp_path): + """예제 케이스 복사본 (저장소 파일을 건드리지 않도록).""" + dst = tmp_path / "case" + shutil.copytree(EXAMPLE, dst, ignore=shutil.ignore_patterns("figures", "*.json", "report.md")) + shutil.copy(EXAMPLE / "data" / "panel.source.json", dst / "data") + return dst + + +def test_full_flow_offline_no_llm(case): + s = run_flow(case, allow_uncommitted=True, engine="python") + assert [x.step for x in s.steps] == [n for n, _ in STEPS] + assert s.status == "warn" # 미커밋 plan + 합성 데이터 경고 + assert s.verdict == "identified" and s.guard["passed"] + r = s.results[0] + assert r.ci_low <= -5.0 <= r.ci_high # 합성 데이터 참효과 + for f in ("report.md", "run_manifest.json", "flow_log.json", "figures/event_study.png"): + assert (case / f).exists(), f + log = json.loads((case / "flow_log.json").read_text(encoding="utf-8")) + assert log["schema_version"] == "1" and log["verdict"] == "identified" + assert { + "index", + "step", + "title", + "status", + "started_at", + "ended_at", + "message", + "artifacts", + } <= set(log["steps"][0]) + assert len(log["steps"][1]["artifacts"]["data_sha256"]) == 64 + assert "요약" in (case / "report.md").read_text(encoding="utf-8") + + +def _r(verdict, lo, hi, triggers=()): + return EffectResult( + "did_twfe", + "y", + (lo + hi) / 2, + 1.0, + lo, + hi, + 0.5, + 100, + 30, + triggers=list(triggers), + verdict=verdict, + ) + + +def test_guard_flags_overclaim(): + bad = "정책 때문에 방문율이 줄었고 효과가 입증되었다. This proves the policy works." + v = lint(bad, "conditional", ci_crosses_zero=False) + assert {x["phrase"].lower() for x in v} >= {"때문에", "효과가 입증", "proves"} + assert lint(bad, "identified", ci_crosses_zero=False) == [] # 식별된 경우엔 허용 + null = lint("정책은 효과 없음으로 나타났다.", "conditional", ci_crosses_zero=True) + assert any("판별 불가" in x["reason"] for x in null) + + +@pytest.mark.parametrize( + "r", + [ + _r("identified", -6, -4), + _r("conditional", -2, 1, ["ci_crosses_zero"]), + _r("not_identified", -6, -4, ["pretrend_rejected"]), + ], +) +def test_template_narrative_always_passes_lint(r): + assert lint(template_narrative(r), r.verdict, r.ci_low <= 0 <= r.ci_high) == [] + + +def _git(cwd, *a): + subprocess.run(["git", *a], cwd=cwd, check=True, capture_output=True) + + +def test_gate_blocks_uncommitted_plan(case): + _git(case, "init", "-q") + _git( + case, + "-c", + "user.name=t", + "-c", + "user.email=t@example.invalid", + "commit", + "-q", + "--allow-empty", + "-m", + "init", + ) + s = run_flow(case, write_log=False) + assert [x.status for x in s.steps] == ["blocked"] and s.status == "blocked" + + _git(case, "add", "plan.yaml") + _git( + case, + "-c", + "user.name=t", + "-c", + "user.email=t@example.invalid", + "commit", + "-q", + "-m", + "plan", + ) + assert define_problem(FlowState(case_dir=case)).steps[-1].status == "ok" + + (case / "plan.yaml").write_text((case / "plan.yaml").read_text("utf-8") + "\n# edit\n", "utf-8") + assert define_problem(FlowState(case_dir=case)).steps[-1].status == "blocked" # 커밋 후 수정 + + +def test_missing_plan_falls_back_to_template(tmp_path): + s = run_flow(tmp_path / "new_case", question="X 정책이 Y를 줄였는가?", write_log=False) + assert s.status == "needs_human" and len(s.steps) == 1 + assert (tmp_path / "new_case" / "plan.yaml").exists() + + +def test_quality_check_fails_fast(case): + (case / "data" / "panel.csv").write_text("region_id,year\nR1,2015\n", encoding="utf-8") + s = run_flow(case, allow_uncommitted=True, write_log=False) + assert s.steps[-1].step == "structure_metrics" and s.status == "failed" + assert "필수 컬럼" in s.steps[-1].message + + +@pytest.mark.skipif(not langgraph_available(), reason="langgraph 미설치") +def test_langgraph_path_matches_python(case): + assert build_graph() is not None + g = run_flow(case, allow_uncommitted=True, engine="langgraph", write_log=False) + p = run_flow(case, allow_uncommitted=True, engine="python", write_log=False) + assert [x.status for x in g.steps] == [x.status for x in p.steps] + assert g.results[0].estimate == pytest.approx(p.results[0].estimate) diff --git a/tests/core/test_plan_and_case.py b/tests/core/test_plan_and_case.py index c2411d5..7f7b42e 100644 --- a/tests/core/test_plan_and_case.py +++ b/tests/core/test_plan_and_case.py @@ -1,4 +1,6 @@ import copy +import os +import shutil import subprocess import sys from pathlib import Path @@ -44,7 +46,11 @@ def test_invalid_plans_rejected(mutate): def test_example_case_runs_end_to_end(tmp_path): + # 추적 중인 예제 산출물을 건드리지 않도록 임시 복사본에서 실행 + case = tmp_path / "cases" / CASE.name + shutil.copytree(CASE, case) + env = {**os.environ, "PYTHONPATH": str(ROOT)} for script in ("fetch.py", "estimate.py"): - subprocess.run([sys.executable, str(CASE / script)], check=True, cwd=ROOT) - report = (CASE / "report.md").read_text(encoding="utf-8") - assert "합성(synthetic)" in report and (CASE / "figures" / "event_study.png").exists() + subprocess.run([sys.executable, str(case / script)], check=True, cwd=case, env=env) + report = (case / "report.md").read_text(encoding="utf-8") + assert "합성(synthetic)" in report and (case / "figures" / "event_study.png").exists() diff --git a/tests/fixtures/flow_case/flow_log.json b/tests/fixtures/flow_case/flow_log.json new file mode 100644 index 0000000..fa8bf15 --- /dev/null +++ b/tests/fixtures/flow_case/flow_log.json @@ -0,0 +1,225 @@ +{ + "schema_version": "1", + "case_dir": "tests/fixtures/flow_case", + "question": "달빛어린이병원(야간·휴일 소아 경증 진료기관)이 지정된 시군구에서, 지정되지 않은 시군구 대비 소아 야간 경증 응급실 방문율이 감소했는가?\n", + "status": "warn", + "verdict": "identified", + "started_at": "2026-09-24T02:30:44+00:00", + "ended_at": "2026-09-24T02:30:45+00:00", + "steps": [ + { + "index": 1, + "step": "define_problem", + "title": "① 문제 정의", + "status": "ok", + "started_at": "2026-09-24T02:30:44+00:00", + "ended_at": "2026-09-24T02:30:44+00:00", + "duration_s": 0.01, + "message": "분석계획 검증 완료, 사전 등록(git 커밋) 확인: _example_night_clinic", + "artifacts": { + "plan": "plan.yaml", + "plan_sha256": "362e754b978adfa67b1606f06bc758843000e8fb4e738dd70b255dc0d87833f1", + "committed": true + } + }, + { + "index": 2, + "step": "collect", + "title": "② 데이터 수집", + "status": "warn", + "started_at": "2026-09-24T02:30:44+00:00", + "ended_at": "2026-09-24T02:30:44+00:00", + "duration_s": 0.0, + "message": "기존 스냅샷 재사용 (새로 받으려면 파일 삭제 후 재실행) / ⚠️ 합성 데이터 — 결과는 실제 정책 효과가 아님", + "artifacts": { + "data": "data/panel.csv", + "data_sha256": "20223bf371f64c27e897b820f9156c27ffce2588585c7d25386d082fa26d7eff", + "licenses": [ + "synthetic" + ], + "method": "기존 스냅샷 재사용 (새로 받으려면 파일 삭제 후 재실행)", + "source_meta": "data/panel.source.json" + } + }, + { + "index": 3, + "step": "structure_metrics", + "title": "③ 지표 구조화·품질 점검", + "status": "ok", + "started_at": "2026-09-24T02:30:44+00:00", + "ended_at": "2026-09-24T02:30:44+00:00", + "duration_s": 0.004, + "message": "11개 점검 모두 통과", + "artifacts": { + "rows": 640, + "units": 80 + } + }, + { + "index": 4, + "step": "estimate", + "title": "④ 효과 추정", + "status": "ok", + "started_at": "2026-09-24T02:30:44+00:00", + "ended_at": "2026-09-24T02:30:44+00:00", + "duration_s": 0.282, + "message": "event_study: -5.033 [-6.105, -3.960] → identified", + "artifacts": { + "night_ed_rate": { + "estimate": -5.0327, + "ci": [ + -6.1051, + -3.9603 + ], + "verdict": "identified" + } + } + }, + { + "index": 5, + "step": "guard", + "title": "⑤ 과잉해석 가드", + "status": "warn", + "started_at": "2026-09-24T02:30:44+00:00", + "ended_at": "2026-09-24T02:30:44+00:00", + "duration_s": 0.0, + "message": "판정 identified, 서술=template", + "artifacts": { + "triggers": [] + } + }, + { + "index": 6, + "step": "report", + "title": "⑥ 리포트·재현 기록", + "status": "ok", + "started_at": "2026-09-24T02:30:44+00:00", + "ended_at": "2026-09-24T02:30:45+00:00", + "duration_s": 0.414, + "message": "리포트 2개 그림 포함 작성", + "artifacts": { + "report": "report.md", + "manifest": "run_manifest.json", + "figures": [] + } + } + ], + "quality": [ + { + "check": "필수 컬럼", + "value": 4, + "status": "ok", + "message": "" + }, + { + "check": "시간 컬럼 정수형", + "value": "int64", + "status": "ok", + "message": "" + }, + { + "check": "결측률 region_id", + "value": 0.0, + "status": "ok", + "message": "" + }, + { + "check": "결측률 year", + "value": 0.0, + "status": "ok", + "message": "" + }, + { + "check": "결측률 treated", + "value": 0.0, + "status": "ok", + "message": "" + }, + { + "check": "결측률 night_ed_rate", + "value": 0.0, + "status": "ok", + "message": "" + }, + { + "check": "사전 기간 수", + "value": 4, + "status": "ok", + "message": "" + }, + { + "check": "사후 기간 수", + "value": 4, + "status": "ok", + "message": "" + }, + { + "check": "처치 단위 수", + "value": 30, + "status": "ok", + "message": "" + }, + { + "check": "통제 단위 수", + "value": 50, + "status": "ok", + "message": "" + }, + { + "check": "단위×시점 중복", + "value": 0, + "status": "ok", + "message": "" + } + ], + "results": [ + { + "method": "event_study", + "outcome": "night_ed_rate", + "estimate": -5.032702345732548, + "se": 0.547128626913206, + "ci_low": -6.105054749393284, + "ci_high": -3.960349942071812, + "p_value": 3.6335376481327216e-20, + "n_obs": 640, + "n_clusters": 80, + "assumptions_checked": { + "parallel_pretrends": { + "test": "joint Wald (chi2)", + "stat": 0.6252747974975572, + "df": 3, + "p_value": 0.8906226863229638, + "passed": true + }, + "pre_periods": 4, + "n_adoption_cohorts": 1, + "placebo_time": { + "fake_treat_time": 2014, + "estimate": -0.32469326228919176, + "p_value": 0.5245409054480956, + "passed": true + } + }, + "warnings": [], + "triggers": [], + "verdict": "identified", + "extra": {}, + "identified": true + } + ], + "guard": { + "verdict": "identified", + "narrative_source": "template", + "passed": false, + "violations": [ + { + "phrase": "입증", + "sentence": "정책이 효과를 입증했다.", + "reason": "인과 단정 표현" + } + ], + "rejected_llm_violations": [], + "narrative": "**요약**: 정책 도입 후 처치집단의 소아 야간 경증 응급실 방문율은(는) 통제집단 대비 평균 -5.03 건/천명 변화한 것으로 추정됩니다 (95% CI -6.11 ~ -3.96). 이 해석은 plan.yaml 의 식별 가정이 성립할 때에만 유효하며, 자동 점검에서 가정이 반박되지 않았다는 뜻이지 검증되었다는 뜻은 아닙니다." + }, + "report_path": "report.md" +} \ No newline at end of file diff --git a/tests/fixtures/flow_case/plan.yaml b/tests/fixtures/flow_case/plan.yaml new file mode 100644 index 0000000..4706429 --- /dev/null +++ b/tests/fixtures/flow_case/plan.yaml @@ -0,0 +1,10 @@ +# Minimal fixture for app tests (not validated against core schema). +case_id: flow_case +title: Flow fixture +question: 야간 소아 진료 확대가 경증 응급실 방문을 줄였는가? +synthetic_data: true +treatment: {definition: 정책 도입 지역} +control: {definition: 미도입 지역} +estimator: {method: event_study} +data_sources: + - {name: synthetic panel, provider: generated, license: synthetic} diff --git a/tests/fixtures/flow_case/report.md b/tests/fixtures/flow_case/report.md new file mode 100644 index 0000000..11b30e8 --- /dev/null +++ b/tests/fixtures/flow_case/report.md @@ -0,0 +1,3 @@ +# Flow fixture report + +FIXTURE-REPORT-BODY diff --git a/tests/test_app_flow.py b/tests/test_app_flow.py new file mode 100644 index 0000000..8bd984e --- /dev/null +++ b/tests/test_app_flow.py @@ -0,0 +1,73 @@ +"""Flow page renders an existing flow_log.json without running the agent (AppTest, no network).""" + +from __future__ import annotations + +import json +import shutil +import sys +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1] +FIXTURE = ROOT / "tests" / "fixtures" / "flow_case" +PAGE = ROOT / "app" / "pages" / "1_Flow.py" + +sys.path.insert(0, str(ROOT / "app")) +import flow_view as fv # noqa: E402 + +AppTest = pytest.importorskip("streamlit.testing.v1").AppTest + + +@pytest.fixture +def cases_root(tmp_path, monkeypatch): + shutil.copytree(FIXTURE, tmp_path / "flow_case") + monkeypatch.setenv("PEA_CASES_DIR", str(tmp_path)) + for k in fv.LLM_KEY_ENVS: + monkeypatch.delenv(k, raising=False) + return tmp_path + + +def _text(at) -> str: + parts = [ + e.value + for kind in ("markdown", "error", "info", "caption", "success") + for e in getattr(at, kind) + ] + return "\n".join(str(p) for p in parts) + + +def test_normalize_fills_six_steps(): + log = fv.normalize_flow_log({"steps": [{"step": "collect", "status": "ok"}]}) + assert [s["step"] for s in log["steps"]] == [n for n, _ in fv.STEPS] + assert [s["status"] for s in log["steps"]] == ["skipped", "ok"] + ["skipped"] * 4 + + +def test_steps_match_core_contract(): + state = pytest.importorskip("core.agent.state") + assert [n for n, _ in state.STEPS] == [n for n, _ in fv.STEPS] + + +def test_fixture_matches_schema_version(): + raw = json.loads((FIXTURE / "flow_log.json").read_text(encoding="utf-8")) + assert raw["schema_version"] == "1" and len(raw["steps"]) == 6 + + +def test_flow_page_renders_existing_log(cases_root): + at = AppTest.from_file(str(PAGE), default_timeout=60).run() + assert not at.exception, at.exception + text = _text(at) + assert "SYNTHETIC" in text + assert "식별됨" in text # verdict label, not raw "identified" + assert "과잉해석 표현 1건" in text # guard finding highlighted + assert "FIXTURE-REPORT-BODY" in text # report preview + assert len(at.expander) >= 6 + assert at.sidebar.toggle[0].disabled # no LLM key -> toggle disabled + assert at.sidebar.checkbox[0].label == "allow uncommitted plan (demo)" + + +def test_flow_page_without_log_prompts_run(cases_root): + (cases_root / "flow_case" / "flow_log.json").unlink() + at = AppTest.from_file(str(PAGE), default_timeout=60).run() + assert not at.exception + assert any("Run flow" in i.value for i in at.info) From dc151b694a7f3707ce61ee6e160efa264ccf4ba2 Mon Sep 17 00:00:00 2001 From: Jinsoo Shin <59598545+jsshin2022@users.noreply.github.com> Date: Thu, 24 Sep 2026 11:36:12 +0900 Subject: [PATCH 2/2] docs(example): add flow run outputs for the sample case Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EMnmZPBD5PQJW1UQAARN7q --- .../figures/raw_trends.png | Bin 70923 -> 64688 bytes cases/_example_night_clinic/flow_log.json | 281 ++++++++++++++++++ cases/_example_night_clinic/report.md | 3 +- cases/_example_night_clinic/run_manifest.json | 25 ++ 4 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 cases/_example_night_clinic/flow_log.json create mode 100644 cases/_example_night_clinic/run_manifest.json diff --git a/cases/_example_night_clinic/figures/raw_trends.png b/cases/_example_night_clinic/figures/raw_trends.png index 2e5267697bba5dc90232b82c39a762306c30d4d0..c57ae93f7997c9c465dfb255f955774fa4b248ec 100644 GIT binary patch literal 64688 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 C@#L=n literal 70923 zcmd43gFx%RM!E!41f>rm9V*?8ga}AUhX~Rw zlJDI5yYKV8|G{@2@4a5(oU`{@bIm#C7-Q~mWhEIxJX$;y3Pt!pR!S9xI!B8_VQS-I z!@u+-2V=tjh`8R@c2#pQcYR{yY=%-aa&@$IaJ98EW_CAocCm7>=fBR&d!3J)+0xb3 z(M5!Z$L|0B#&rj03m*UIb@&j}1xMLOE+`Z=8S)y!Xo|m(x79f{ofnMZuH;z-v@?-WB>bQ);u8v z0g`{etWsw5|JRSE+-@a*c!xF4us8YEd!pdH@83UJu*AtS$H~T~r8z7+ehxa^U&9ZV zdUijOO5>L>zj=?p6l+tSMt<+fNkh7dCm!G|Uo_4 zUnsRazQZggMlW%8%zu1xvbFo|VUh?vT2=gL^^@gT>DBtPQ!goL>9_E2o&m7he0j~y zl9$-ojn7U`5=GoN4|jhBuCCguh2EEv8nT_LeX5*4q+M!BYRdN}w}pi>UNWPxW}_ZI zUM^V6z-MT1mShMYHcBO$T+-5S;M=#YuM%g5ed%{m{taiRNe;_WQhPnHCQb*NMwOM7 zh7*-e?}v!d6Rzv=xd}_TF+$9ks_*abFAW#cxo=EG@2`zxp)`y2 zqibu$$YrGAa?IKyC{YpiQxtf?;h8L+!ua_3QA`qyjecJZSo6dnqCQ{x;e0FAd;0c=7V(-q`9?9UWQtIU*t= z>_tBsLF-rU1iNs>dtD6&bNI5C$XwSas&q&CKbXjrS`W$9tgWpT@?U9#y~?}Bb*o?B z(^KR*{jFyoOgfsoyYbD<&6h?$WcGfTpP%P-m=ZgOjomyvOr@!*iAzAxI_}UAJvgWy zxJB`BrrfXz)6~MkWVSgN7Z2}QS(%`%t!-bG%VMZf7kg0S@=$(%nT^)y=x8Ine{HO+ zWgttQ=`9JFq+v@4A&syj^TfnNx-v&!jtW~)P|!>pwSmAvqSo+x#Uu#65{s{ls745n z+?!WE2jf$h?#bU4va+)BSWJ0BLQhX1Dk=(hMlbHE{#dI}(`0XXh(^GY1U}Kw+&n}& zn$~D{sbARpz%iE>qQJ28#pR`M*~H#HK1TNTL^3imu4`kIOx~LqeEj@-V*>UQG$)69 z-|V?4@KS6zPDQWge%!*iV{6O(Uf~Vq!-o$Olag9sX~?fANx|~JS?k({n7qdtm+p6V zYBpTs^}A^Q5ca!Jjf$E&bTQ435d#CG}iUoyGc-f)%WB> zNeK!?FYJh7laA^;eg0O!>e)qp^BD*+Pdlz{F^^5LhLe3drKVUi z8$wV;MP;n|$@)YS^3TJCk8KQyv9Pds{Z73vUA}xVYVFI-=^)4+Gfjc=(S9q#g|V`i z9G#r{xsJ9vm>}uQtgPU(oP&=Go~rX|>Fm7l`t@rh>TsC6{hvL1#>bR7@Jj49sz9%X z%I9!5;N?s5f`Wp+YBwA2-F}&|Keq*F36bRBx-q5qI9n}SX>3}<3J(vj#QTr)?%Fu@ z_3PJf@bd>kVoLRxyO4a_r+U!c;%jQ#v-9|UnKHy4US2IdJp@WhN@ERm1MJB@o7LrZ z;~^MW*o|#%=YRhEX*pbQ@#mK$t>q(TL{k1Z;k;2DXKli*mJfJk;)_FiGWJ85zr}^p#I84rKy{W0m7&0BEzsGDa_3qfiRI#d_4lI9JdHHWvFO!oCu08%}`k;#O2}{su zrPKVquWqNuPwP6&w|{g(rjRdQ&$t&FL?Q`W8-87{`qk5kiYZTdp48IP($MyA===Ci zE5p}P+(V47{^5VRZJKfzAN>%Q| ze4AW5?rYcDdQ(LA#!gO;HZc8f+`LKc&~W0OyCyC!e&5&_tu!7XMl6?V0m;zQEtKFQ zYY+(<_O7SF0%iQ;a~$i5oh{T*LAgLJAA&Tievi2&q;!VgR_RazkH#D6u~7} ze%t5JGHoS9pAHif^Bihx!sY7)$R~CC2|NZ=k`&?+c6N61;xVvw!tSeyQ(My>^Mn|8 z2xtXz25m+^nnj|RlT%Xmx_l32aWL*c#6?~pXMg2+3VR;DySuwBR+`0l)_En3d*?}V zLc*29-x~(jDa8$^V>7qr=H{gGH4EkJuEfc?pIxFm1hyzyM~J6gM$q0G@G_QYx=DJd!6&TDg~DdI#jOcn=fl{#D3OUnZY z<5D-I(sYeiIPjeQZa53?>r;-4jb+~6*}3oSU90`cJle7!9gJcgE4RxZDT17U>3^`< zLhQCrJ-syk{rkn^%^cO-pt`y`6f@)_t#zAge**Ffk_ujgW24YEBSmwYvn{MwuE=Ch z1z_O>sdG}Iq#ArrS|O3@j}MpTZ#9!MM4O|@VfnE z_2oh9vT~)ncLVm@2YY*!xF44*Ow>MAqoAUSsH_y;TFx)Fj`;vbJY_yRJ3A8YI`W~! ziF)!a7wL&A7nvUm3*qoBus&IH1>)d5YIbFX z%YAi}<>SYXQl6gGaLh6tpFA1)bE0{vKV38b{(O7%dDK?F3{$)g&!tOJC=#B!-50&5 zGc$%56qi53*IoexaNpOrJ}fel-0yfR6abL*n-0NCuvn#btCKZaUM@C9Mi~jKTuP`{ zwN0Wvhozstex<%};lf-bPkkRdNw#9rduo@bX*k5G7Bh_jb7_93{YNR3(;QeNka{WO z;^L4E(XDY;tUZ{E9Qz0dbvY%IGdyHY%KG>EWOu>uW_%t?gP1Ar9Yv>;LpK#=W#;P! z^#uu~++6+2$=nC)H5*c@sxSI11;#|2oSeok1;)h0jMn?qeD3L?fbcek0@6G_{&@Tp zk}@_^LmoSAgdd~Nu8I?q6?bN#Tv0n$>7qy=gLsd&9QMsWOcGK&qK}D)AT#NBK2(*P z(7j*`=w+@wnvRu+=OGeZu=Q33FLAsXSn0C(oP(R2qQAf2 zc&yZFYpW*vm+V`ajahyKA0oL`+QRl9H3N-9Fh>TeIm0OI?MMAkY%Ayh zs-wJ*Ze+t9kE68;u47Ra^cm7|2+kRWV`r)6)>S#Wxl#W4^M_Kz7Zyy&$np5VDdpqM zX>TtThOcS3zeJYFj zHJGbLsNdr9Rb0y&J)81ms^2%U2v_YAEyT7_NiSRpn?Ks&CzH26WztQD)P3{GzKv`F ztz!M5DRscNORwU8V}Ih@qMSbdJ~kFXchi2UEHW$%iQRZP0o<)=#YcvQ=}p_+2|PA2 zKKrYW)=poD-@UF|shr{RI!ie2YP=lRD`9Tm6DaTG)I9owTn&K9EC+MqP|Sb@v(z;C zFm8D6S_XFT_$JfS)4SS-1?T9ZGoEZrVW)+Lhlh_jm}fssv)>dhuG@dRzc&ZiRm4o= zfdIvC)kIzs>0CE5GP2UTuTp$87cRe}!^2HKpI$(XkS5 z00BRq^s^~Q-i@8M+Vz)l$&-%lh3+ypg3A-#s@n&jEWQqNeT9>hnwmN$GVQ$+t)CJU zbZ%|>B(SsdJU*4>OY63}dyE23b5a--dtEtUFRwhbo_1hb9a7$|Cb--bL}J|a>MAD3 z@{qD>=kIwOFT!}OEtmJ`4Kt~CkuCxxaVlo!=Ez}e>dUxSw!>GuH~biCVR4@mt)O*K zs<;;>O7h-4x2}keTW4@WAfyM^C7gfYq70!lKosIQJ3Bir4_>X?hMEMh?)@_@OyPftA-Hy7ac=Heg1+YkREf`#dtk@LNHG&cPFBS<^d~+?`{z+9 zTivJiFYnP-;+HtXQaNuUj z#0Y8c&&Nn$qEbDVudeN^+-;NJo2+!gTKt;E^ZV(^@iEc>+&R1TSYIRD0qO;#ZptX$;n=jyvvYOi6T zE~0MOPgL@Cj7ls4k~*)vOGABDFX!yy(sKM~2ZQ2Eyz1a&UyX@?F zIP?q8^943)cb+$mgs+WP+{;y$%ut>QB))xP)E#n4$%hXCBy$<_yIkmlYDo3+g`Z%N za)zu&$HuIgzT#Ybo6r<{dU6DJ%jkJDWGz^3KZ%D*q5Mo#?J|H;v>;&&pl27+Guyb&fh~_ z^{EsR4)RGgfni;t&|2p?UA`jlsiFcdQhXCCBhm>n`5v%9!nG(#A;ElHZrcP01i|fG zTwEz8Ym+t9{->b<5N6E)rQs6C=tQ8=h78=;+>AL{^JIjbO-?CU>-g*i`NyGGu#9vQ zXOQQxP!p4rWlX08P+u@mDV;>}ocjGOv2(;_#7YXJuq(a|f+bwOYMiK*RL@t2e%j93u7Z4I1jQ^#kN1Q zp*YV(A>WaTiHj4yrrdD&_Fggf!Q7n4<+KikXeAWoDT+#8aMv>$K$2_G9Lm24W53BSV92>D+e^baQ1R`dBF2iAquc z>wzgL4ApKcAr;fUHNyq+`eP<(iHSKr3eeQ6{+##QTh>7Jz1b6EW5W{n+Hd9mDi&Dh z^_!K2C1dc-7x{66*Lkg4+gL7-8m0$)D;6}8r2s4j_U>#$j+B*^^;hQC zURGi`KfX)_`UO&EDRaoJ`DoCOwD@MXji{#F)PO!3)wRl??7gFq z??)QYz?#*wQ=chn?oXI8)Kpf;KV5$1efzcUMKfsQ%<891O-)gKKH>^;a*_~Y`X00A zkN+&D!S-{zxkV#;yK{Tra00#{U#mC-mIsmruUQub@B&R=zY+s%Yb(;LA*i=_t$tj4 z&6kl}W#KQ*D5`v1E1lT{#~1EP`rz;oq1u*)@~MF?+Z*#f{FOag;lKpM)z7bKG0;4` zd-pEAq(s@Pme-{Hm1i%G7dOS(<77kNmDp_U>_};8BY+S~7IDM7b?X+CZ2r;n71i4d5U%1E4slfAV2~9fp)D2Z*y~V?Ay0^@p4&q)73Lz)0pg{2Yx?TjMxM$&yPE}V`4as^-5uQg#MgiDDdwYA2x$rBFKR%NR3k%P{TcCP8*PjH!2ot66 zcjD%~-FancAPXO=k6U}?>%FyyPLi_#)S%xQk{5s)yt_PvfByXWgQJaxvIXcOgR`>C z_H-RD3s?{0qWTIS-&_m*5PI@zukhi03cnmUN}6x>7=?%`19@#;yp99h}QNT(y*Df-x;xZE!Yo735mKUhVEr9h{D5x>T4CTrN2j)2*tNVci z0|TWlcr>bFQb10Sgc30{cfP!0WMY!Zpo($F;y`LkDmMP2kjl37$2{_I%7>fOR8*Qj zLwDaQi`|`uS|rE7=C?5=u3PKzVWPv;-Mt;M6%e~bzy{KzfKyG)!+9*(NnE5cyR?*( zvbhQkbdK^kv@B~ChFRhk^K)}@Csc)EUo-Mr7gtva3=9lVeZQ#|npTmvG*sIE|aT|{yP zTwS1Bk$#;{)(@@j*VoGK$#?A>xjH)DiKMz#dIWq6#;X*9tEo06%Ab8fwA3TW}L{L!0o4F*e{~SH%=PjzEg3oa2Z!m~2#7p@wtusmr@?T#-e)Y_A}r?=@a9|h<`yme zr}q~F-L`(HW?d$ShV~0zM-}ibHdk;TSG!&X9;H8Dizme?rR_m59QZjX5F$sM&A=-h zo#px7+gZwtX&xD&2Byj5S5|_xEw<}DY+^=){wF&vT{oCWe9;_$qawhTy8hPgv;`;nYp;| zAw`k1E94CPky{4p9W?`BI;*!BzJ%tpDIbK+EVRag+Sz&eeenOI=x!3BHh~_dS^eSy zWPmp`x+s6(#+C?NEQj(a{n0@kf%&_rJKo-60OU1_w5fRXZ5fr zlf7X#CV;yC^<~5*?}+IJjF#e1WJ!TP%4amaEVjCL37UM-U-nky9%L%FG|cRJBC4Gi zA`+n^-Q3yvR^()})baJ}!=9v@44yZNligy+#msq87>UGvn6fXL0v%q*V(zykz~ z*0QIbo*r;IKVCkJ0KyHwOs7z5vF8o{n2UI&y~hKfbw`SLrfZ%!e6Sh0?gVE!L!Gk? zDsLMQS#2P@wZe&R142CgTlvGZ+d$odAR;G@Tn#5G%%~;gSA6_&9iTQvgqwKw3=`oZ zU}IH&&foUhx8=1OxP;VjkR@5xryF<>iqF=zK)c#CcmFNpvG}DcS8QUcOgmqw&R_om zIhz}Wd*#ZNE$EBFkz=^mwKIyaNI>`gYk9pln{eg;1R<*cT2N$SB8}7L%-yXqo8s{F zbmo+D$lBDXpp=vp?K10lStTW^r%#_E-6?4x4)O5QRmR$0B>Cv<1F1*}?KmZE^n0Co zc{w>Upy4t70ZmZ?ltj)eK$Uy8W2Fx&Yid?n6B81&rVIj7=NhKn*T$^;HW2EosoKcM z2o1%__}4(z+(yG06_jj*g93HMV0F5|;2$9dAWbq?{k3QaV37VIJ<+w5&maIusjI)@ z;NTzu{E4*n#vmKc4QUw+S;0k#ubK3NNcF+@#4|26^*S`jtaQl z?Bof?9gv9pD>H@KKV$wICc0D313iC@8|Xw2H!@7$p3;$reNc z3KNj9hbzVM;xe#-C1qtmssidS`+8JR!2JPw&Hi@}$T98!GzbIAWx4cAZ!Z-<5KMm% zR48Pg)04uQ&cSUM1I382Xmn}7U)XEE09la=hiNKURLXx;8l31x zwgNA2CZ;q<&=NS3eV8?}x}vDQ6IMA3PTAxXTZ)9=)XdQ@AbX`Y zrs~?D!C(jYcOTSagoNwt>@)`0fqwMK9Md1s%bMl^J*lHegoK2$>Jvy*{?J_RcijH* zpugBaVh#V96e}q#nDJnaiZMtB#vlRAy|G9og%;qveltCGJ+5q&}hgc=8b|0Xo8E2Ypleaq(uq-OVrEk;cB z*NR?=VL+4cUu{NhN-B^<(ssOD@o2WPAPvXiZT?$J{o@Z=W~ckkXsloxEK-;m?P z1g2Q-g@i$ZNO9Lt6qu-XlLyt5Mr<06JlF0hMaRx(`1D;J#@fyr$XAYzjvQsD>30K5 znd%AkEVjP<36ubmsOa@U5Xl^nK4Wx^IDYR`b3GQ~wMxf<#X1JvNTg$bcUK_Mn_Tku z2D+39N{iF#=m)$Gr=xwle&9v;V#J{lsB?$pHq1^;cmg_S80^wgS-v&;ZrHXsaaWQh zvhvW$bMG9=M9~;+hU9UEW3vP$3fbwrDgG)+G5Hy2-I~EsM065)vd)eAOV1bkJS8KP z#B~CSn9txLXwPl1&xGZAb)IFtivu`m32lD-W|wJe>*~6__15|_TJBAg6XgCv)(#=u zkE%6>zXtLi13YgY82C7N2zN>a>5cNv9?-pKLS2VWKwVQ`*S+B}F&A_QI;))cP15*> z(Znep$6Fo!P~Krz$>uv^IzU5+d;K~jCx>HoqKX`G7XXeA(a6^{TI@{;d;VP7+L{yi z%7Cr;j(MoiAmS&LcV8hLl0bToOJo;-Bnkj2hW1YX3v-L!=C8(Ig>=uw=biyK%QCESc{5?(9+B6i9N(pE;GAEeN86_%mws-fq@>%-R7Q;5#OhQ zjw=MR0Ft->83#cb4GqPSl$1n@8FHbBh!x+v!~8}e1nzqlii#v$uaTV{A!xMc&Yw5x zN8iUKBt+E0rt)c@-zo0#{=ft^HZ@^5SJAJpuhWRQatPUu`a_qyq_Fgef%c|-6cp@P zfX;!Tp)$EPZ3zjHk@!T6Vqp;9#*l6-Cn|Y+zI-vVu)y*2^Fz9S$k%wKNdqpn~V^ANZpekKh(J2h|=n zO-?f41!E$YCF;(NbaWs`bV)c~-d+7p{^hOUH+w{d9E`_6z$7%JMxb3RegAX?;-jov z2!IL|aFH^mrk6Z_uj|bU1K)u_MA)GVF}DvnfJ{Uj1+Xi->yvZ<7F(c!D;g<+52|!t zz(X)OtQZ1Xsi>%+=C|PU)cQkSH3YW+<@0Q@t|KuH;$X=DLOP+pZHK$1{MHlbj$aB|9tPR!p`d#}(0YuX3M3mObW zlCH0>PZYGF0(C^UDBkp)>Q(sKAVB+Quixtc;XzGCi6T9ByYgFGcz*?3u*+wO}pOv*56Pqg18lSTc&7_VIl^k48dGkU2UEA1fU<+ zh&2nPpy_84B!47I_}#8{U&DhO55F_|{d)$4kO#1$jtgCsNMS9u1NukEVTu3+yrp|t zhxA~dY*5a3j7G4fB#J({jtJ+u)cw1=uHIX1)CYj339kppAg-yhudphPfEo%OR}dkI z%<(8HiU2MH%Jdu@I7u)`STlFM-2qN`u4*wAWFTTVi$=?iODw|$j{+@HpJD@=XoodZ zU?5US(V7nr0WHQ`2|7d9(|NC&7FFy9FfEJI2Pw zW!DyLxD^Bj89u1F@S0gIgb|JRbu{y zwr(4-Z2;w{-QX*ZMx$esk|F`pfV+Se*+?i{*eJMv`xJRT<;joML!7N`ZAL$P-oU46 zyrkXuXB^v2fAxw2?)dd13y8$g+NUmMU)PKYR^g|FkD`BDqWqzZ_PiCu2cgNlY8eh# z5)u&LUa=A8L+oaJ=V9~cMO<;=JMThMGPAUVix@s?YD{3a;RE^09j5iKyd;p4B1}>7 z6hM~xq4*v}s_&sypxz?mE-(`&;L$;D0vHbUZ?pf5Yk+`xPVDQTOkcg}}ymAgNUha=gOvFWPZfUxv=XEu z|L=1(Br-420_s7bz*)4AXP_JN@-ErGpAB7;ejp%x>lVgAu|-P@BBy3E{P(UQLiiD! zUf)ZpYfDxx>Ip*bS>*xTFzODN5fIf@ z;SJw;kTlw%k|F(fohM}2gM|cxK7g?%!Qe0Z`x=r0YeTzO1_>sp>Wz@O!vU3p1Oxph zk*}{WU^^5x5j}3WlyOgN)b$%TNKlS{w#~?8B&DR(f8(;GYuc2K^SvpZ@^c59TK{K}%oB}zVQC5dan`uADbBxbQ<*e+ zC54E$yh``ry@Yp+GRPWEeRXTZjih)I-&|FoL4GgZ6xJWet0*U0SWRn`fF$!D-;iD* z_|Xt!G6Awi$SW@z!WZBX3I!}gw37(@Ze9lk-YsdORrDuC`1rC=m8mx_&d&e)98=Y+ zItQMA_t`oOXVCN^DF7 zJ?+0+`2QL>qg6tc5W%Y1ZVG@uZCDUo)-q?7Ty$TF?3()sQDJ&)_y6u`PztfVaZUWQ zg>pV9)ST~Wqgiv$c0c{=4jp2ZRut$-5gkd)X0t8+7|GJ%f( zyGPVhkvti=WnS-tO}sKEH@7xGFV-CBv9-|;CSaoCb)%7z5|m_r$yym-{NJkSPQ567 zWpz>ZWS+yBl{KTYJ9KaWQ>dwMM!1BB;`u&%v|^U(EWDl48ssC5d;RJ?&~F3+Lu$M= z*9L~U81PH7=Iu~8-(p-Fn)-XNRIY*>))EiTY4NB`R`h^62qcTwKBr{sMbktb7n5atf;cX1IG-lJ)KBhMVk{=rmi<2A0MvilCUlqawpYu-AKk+jJ}Ny?RAX8Z62+g5HY!W-XkTSMzj-9Z7bsOMMt{ByK_5 z7z}6!=za|70-C|NQRasCK|P0sZF%h=)p_ zBO&0i5ksXyiU?&it>8eeDAY-KBMJrfp)kY_@VFezAElsl3^)OaV>?=c;kCbdS4W5T zt&m;BRkio&z=CP`9{;^^T9Ih!8D(3sdQpbw+2a-MIm6c!7*tb`Q$x~BlbwO5`u;uJ z+Kd6E^~?Q51IyhX!Y{vm-@F0x8kme|5W^@4rI6B<9uMbfaIKvoKoh{goy`8FQiC#U zN<^Xo^w&6b+F&#J(F`B)u9oRM3F`zrwFxYw2>^GX6++$50Q-Vd1`7pfDMBd;FoRr2 zTtY%<9GlD>nD#{Wz^Rt|vWJ;9F9zf>`zwoK3!EUhXCl1E&W4!lDL-6&RmnH zr|S}XKTSQBZfRIgoSmR_ndo$KUE~T2!4k?jRL%#Og{Vv?8v5f*hv|B7t=t1dhJl*) zJ-P;j5CKrg3Td|)K#vWD_$xAKcn&lGBOKD3seY$V0S)PU)k3*$0kRE%Mo2@01RzEz zQek%E(oOT7Ym$k=d?RygGj6CD5kY!P)J@+&e->A^Ry1i4EzRDBfuq7`k1eg>c4ss5ngH2SoZFPFmO)h}Nnq^2&o zsuSZPpjVOXa{=E zX?^0>KlzCS6&o9iLP2A9{deoD`0)AF@1H>WxSy?*;`)N%j>i471hDvtrrcLgPiUCho5D^7yyWqwz(^}O&+xvBu6;@io<5l9kd2%iSG%u^Fp1wH({`wj8LpePr z0FHq9_1^-I+8tA_e-zs=(R0}o{f*{rj@XEw8|VW(e`n8mJM{3e1uTLsJV^M_&eofT4c+T|PJ zQmR>hh>}Bz?`#iGQobcoWld;$+S+KHV$nnq;V=dnmV|=hT}ogEH{{XJpFaa`#@bvd zZdi`@1DY0loRUY;%F_5-SF4IxxT&Qj2OwM{i|p}u8xS0c#j%lMtm5-pPdSW`Iui*g z;U|lXsC2&_cAC{`_3Wu-r@&cryYu{2vs3u$w{-FIFw}%^HE#7!L4^}ee^O{L_E8K` zUVv~1J`oGZ$qwjCUAuxwguzSz_MUf>S`#ms+1arH_w}#Cwst`EM%c8#MQ2x6IFCS- z>dZmscn_S_u`gac%T>!pn90ZP0MBXxmM=WT{hQL4QzFBE-|$+z>*2dAZoYo3mSl?Z z-%!#U%%2{edi@}T)^L!Kka$W;+*V>p?CYC{88hyz&a7xgoe`0o%VHL{&4O~G=7E5f z0_Oz@bhv;SGeJ|{+}@Uymd3W9szvzNbLY-kI&6=ZS_Q&EW##0=gGSNv+j~MFE1)}x zjgJq8`ZEh$hUelhaQ2`Kff$AMHKBZ@89b0+oL<4}0FzOw$B#G}#KVIOGeGwPPDBxe z?1+se0X}%-VE%8LrcU>RigU|GN}yhX;kjA`8R77+U-Q8innD(ZGnXv}rS5?NnX5F- zI(2Y@K=JIi$a;ZpWU0g%pKz2U1cB2X2ajHFJk?K!pS%;HfddE|`ef=U4tTw!fYfte zz|B>U06Nm7J?i4vp=vG#3}1)>Zyf*`36Ty^5t`+Q{1Tg*YJa1@4`{twP-G5%CONoz z3Ie#**(+@p<99h)vcD!nu<+WTPgR8gYCeb>F-w|B`{hv|L3U5a(rW zDvx0#0uu$zxb2**>(y_3vFxwlZ+81DfJP!W8=2B;$v%z z*#ssqgOn5Cxm9uRfPME_Z+&m{ByYa_V+CA1e@d;;8yBEmlGUREd6aAUhdK2F(8G)FECIU#!^G8m<@U?qV0>&^Eu zI~-BEl!Y!xrM?{MxgWT;Y3oHPpSL*OB%PTH`}|amC2pzyC#Y2 z%TbDxyX@#(B8XsF^?OCx<};!Evj@Lc!8+GDv>v1Mpv7Q>yf6FB`i+GTcE`~Stm5bX zH>YK)%2|f(g`VG>{1th?_7j_6uZGywMY=wqb0v45R}PKv74i0f!M%Gx^L7+OCbAC%*0b?M6NChT`UO|m3h>+f1Q8|18bb@=C3V& z=p$VBk7|UCVI34MTnPFUpB0YI?BbI{8$tJj{hRnA-@BgeJQ2qkRQ=&{zH0Y)nN28= z#s&_4&dz-9>l5MNW}Jr^34{SMG-L(>CI&c?5!nJryXv>)vhci!&qn5*me?3FlpO^) zNLB0-Ud3ZQcH!4f&~_e}YNjV2FK>Nl!E=}8rLn>aRpL<9wL_sD^NnDU9*&JJAVi8( zpPEh9J|%~HzFH;*0*%8oGDv}_@YZFgqiG^oDyZyfujdfDRxCK zAa+DyGehU?=W5ibKXCC;O-Fg0?0?XzIg1|)R$FFy!_ub*yI;#3p znUK<*iod5DC@Y{I1fo)|nO?PP6MP8s%PlNIQqs8G&t`l<=gMv%5xHADI3afIe2Rx$ zV$KisI_L0nfMp2yMR82R{L}!;kV`#iAA~DJ=;5e~{7@h;wY0S0G%^4I9U336Q5Ay> zMH~Zni1G(>%%FM>Qe~y2+`7IRCEW0vHt;+Ww=LwYGa-5Z1W%G2N0eMj`aN>b!1N7$ z*9U_I!ePQt+eL7{-5528P(!FZm@5M6Jv5SpOir1gv*GVJ#=bg)oMEA!zN&rWzt-rS_%yPD4Vqwe9gg}FIZ zwhlo#G&maZnmLa z7gQo>HV|D><0>PB7|e9Aii{v1xD+ds`y20rXx8f#&(Y+{1ZFT|wa`jpRaJwyQ(cA5)n5>b3BaQ7vmx}TnkA6IAX)-R$FdK+#j48 z@zyq3v07K3s!xIt8v`Q>&`30aa~7Z(l!FW0+}sKbZ&jeX$8xCKqpCmIe+&d*H;gNi z4$%PjbnnV_%L=TwUdZ63dUP}S%PoLI+FmSVkIn~sF3|Bv!w<|L8_fd1YZq1w_VDz? zL@1N)NpoNy5mywD?>3y=nVlMWh6w&H`=Y-~R`7*lh+d^_4JHafk7xsQv+G%}{rIs7 zY%QWEAq`JvdzNNCVk!X^*a&1DX%JixTxRy;YWyg@6XkSTeb~#JSh5V!oEXcp+o;T*}_xq||tHOkYT6&-SiGrl?dLlQ!GXed@}2+bKAf1L6a zb>aV4$L%A9)&=>Un1>{QM?+Yn9PN#OJXOwk#5%TxOmG3H8an}pDEJ%kfzI(kH3A`0 zW(LkQgrnwlTUPh*@K~bXfCGTAz5q7QPNgd`Ilss-`2>N>0uOu+KvIh!V+HX0Q61PteOfn6KDXYAGUq|M7Fi@u(XQ#yA;s4m=37P}sN!{Fz z&aSRzaKXmL#bH2>f(cA8pXnCs1B>qqYBun%vvD$v0VJ59t=*B7!~`7#bV8gI)All( zk@L3TJz(=(eDn^r-)L(8rU+|7Kx16 zz>oklm{%VE{P80QBppV?c-^arh9)0Cd(hs>2ph=F7WNlhTwEaUVj~`9!1>W0m(>R# z{(-_kge;)hDw@}Jc6Jc*AF=TU2cIe0n%LMx0_{Z(r|fmFR06cbNL!1@Pw*)iKodxV zWKdoU!&L~44vv1zbjztQP;f9%pd$wYV`8(izcI~(L(DjM>nAuRqM;+Fg-nTs55mY%AJ+7! z^1?~%JfkC9x3UO)S7FhltMA}I+8MUI_DPmAey;BKR155XC^$&w5BFAJGA9&NLja9d zj;ZIXbe}_4V+=VPhE|9W76fQ-Fvq2XySdDrGSE88^b^W`Yzb)qL#!J+aEPSOr9v@Ly-IgC(v=eov?PNSF{p_ zEt(dTK&Ouc-W!~sBtSL=fhYwd<$;izje8P#VLmA^IQTrw(;$Nwh^`3=naIn#*f=;V z3@(seKp9_}s;g0Q2!RO+v_XR}cn|!+qKXWof`c4@$_xmuNV^ZxYB1m?ttDQBb4G{E zO6y?z3X=zGKy)AEdwpmS&}$tE&KN2Bzi&wQn0Xq7c-H%o=gQ(f{^qgp9i3#D zI=q`HefV;-)UqEF1ok#aZzObd08eA_(tr#CYCqv;FOhPdpvcj2G6U}?7XSgJA(3VA z;jEb^?Fpv{BaOkyVetz&wZ9mM64rJP&`%t!0xe;>&ieVzqa}} zk79_Md1y-!x?#$5r4X-7<5Vndb?tFW_mpOh0n5wT??iUckbI)~R(;;w%_`FOOYL{B0EK3_ zGPCa27k69^j^$Tv#hhZ#&IIJ`E4Q0`NHldTKi&50#YM09rg_!cJUFjD(T# zIgAdMf?8NC29OHHDVEj|n0IKrK0Awn(872y7&+CN1=D6>;o%s{d)nb)%dGe={Iz_U z#w|l(VNv&SRetg86;L<_rd<}K4PbM+3CP58wbGf8Ga4Iuw2i%u_i7IMF+?7@Ubqu> zgaCOoMxX#*uFX0~S1v0^wtz^=bm=N(^vupo1bO z1!gB3LAW5Lpa??Tn^RL$(ABrY#UpGD+_y<<7^z0YL-v;$z+ynsYyoAWug>cUObuj! z%L?+}$h~p-EY;eR{c$AU+<;PsIO$K0euwH5wu1-b159V28lgC7WeG!19hS3CDml{{ zJ$|shd*8N>B2cnSr&!6vi%wyKCJ6q1rT>olV`!J$p6BOtA*q#G-va}Zc02e^LV#CS zEKbeJx(uR4-lV&$Yb&%Fyw-ypV9g=ls6Q%FIsqFrlFe1Eh71b8#~27!*z?fYA~SD5 zExke@Z`Bw)h$0e*jgGUE=`$nP7m;pLIk*RCMLl3LGLQbs-#W-xQ>5E6keThcFy&Ln z2ffwYH1NbYCOLlm>@+!=Cg_ZSkE);J>Xx$PDg*_=mxv5{0KtD7%5My~IGEw!Kt2Jz z`3^MHh=bO~W^&7lLBthV@(z{m?A z)S<^jF5m#PoU9f^Vi7_J=o+9>6IK%2ZXd1ki|zFABN-920svRgjsjwUdGOuk3P7v6 z9hZ^1GMCyibKT+OW$f^r9@jtWmjeO>A=a@|~SE?}2qR_7lEO zq-m+)2fN&@*%qw%=_E`<19~dlHR&V|?WZiz9O2f)nYH|9Fo3^MM907ojl})ct5*g8 z3cN!ae7O{gb#!k)a?cB8eeuFcgyq7P)tfVR$_QUFzK zFqB)dE_vzppB06w=efhU6P;bs&`hu0hDXl$eyRI#wj8}oke1WS`_!NXPAf9Oa?o&g zB=O_NkH(=PN_;BrMyOcuC=v7JO{4^b1tuBM17V7j&snNSr%z`q}(RUj;w@z=gXBGg6OQ?$bRm52KSYJra9d!(n9r4 z^}^fHah9LHZ=2@1OjQLw@F+2kqP@r;)`|EeJ)Lju2_Jf*B1QO0h5RqE162NVC?|iL zsI4X(rpD>%=|sekp~3)9v~W6AEpV@S9Qqo_#UbPG=Y!7(O%85`ll)kZ07miG;L-dt zVb7lv$Ep}FesQ%c{V3GQ+ovsG+nNkx&OM>CkP$2XZ|KR$Fr%fhz-a@Z z2?Yrs3kBrtT87=@QcIP;ju~JFUv~D@E4rlEp{rT7a`Sej0>DhbcSw&x8-2%Eb04cS zkel1LRCL0&z;(I)<8O|l$%i~6PJ~}#`kFg2CX+mtcO^Mat>z5z>2|7lRcfVJXLX4N zXW;Or|9SG|JGg`p+S}vLLPGbXCa%go6*#3TdhcSp`-;6va$A`5wBR5)Zt95Hi1El9$ZmZDCE@lJ}y!}<+Xk9ffv=H409eabfBACThM|2 zi^ia2$ptKVar5TMc3Ky&pryf@SPx>giW8Of`SxrxbZAUMZ~eNWSVruFGlsGBaki$( zVOl;c`MYE)C;yL)gB6YatFO#Mx7zD$I-ZuV^g-}Z)!`1CtMzJ8g5{Pr@^)5yg!-V`Xt-a+mu|i)?Xb5 z4J3}t!+FPvFU|~$(mEkix0!H6U2W!!9LunoaB4*OB``^ch@koJNfoC{^Vz=$^Y7pq z$yO~?|N+XNud1o>-9auv6*DXRmVHC&c4LVtZ47D1@%2@YVK{OKkIp`&L2Ro z5@-U>7GPULfs~ztM;hIQUOmc5F0W&KsxJSfB?ovB1i_!q1pRV5SaA()9v{-#n?Zqh z0LN_*_?F6;#%Z9OML`n;Ee|FTtb3=mQtA>{<2hGHf5*5V{khu0JYP{b=ue~bA;ZOl z_>%bo&1&3C(;w2X&!1syJ;3vXw9&}KmqYCqHj)#9pUKxN4Ge z34xT*vpi7FnE|8+9cT&2la2sp8Uo#P0E5vbfJ+eD8PvUYVeug~xC_T8Jf4|*P_98b z%23UPSpp_QXq*LKAP-#o_7WZrR9eyhojvc%oa(nN8xH}YU>P=%TV(?sf86G9wUf5G zMOgDrqJ~44!OGgQ(4BxF6WvNj45Z>ie?b&2?Wd=Y?t;-cY7{dsuZjgiy;E^(`@{SK z%!}iI)1@7zP{1kKhsWRt?r`MU8=|*SNW-I30|W-Sl`4V`#|JoV>0PyPTc+Aj3rPaH6{~oCH#55X7(%`08LI|BCq+f!(VF zo&?1V1BUQ-gz5qZgIkdi^hC%w9|;-RGvLkB!Lf=UR_Hn&GQouFQf;h;)yabW9lg36BK0X&j_Ja$iOU3-!0h11xg` zny867FRg5QFy2Dz9m-RGfC&>D*G9JFlL)rq!w}RC&yaE3H>hz(xN3NAj2Oa+f>$(( zRuB*262bW#1%~#OlzDiF*nL%1vZ<$wDWUBzK|}>905QE=oMp);Y{E}xL3BL;lnUFRuydbq*^J{z5EG2jznTB8jNCNabk z$I3O+*3|F%94+L3hI~*p2;Aw4A>?SW3u4OB{K^|X~0h4jUzQ@8~ zFVVNwW2a?Q%2K_zw-H^e@PCLp?|81;=>LE0y(w8CD=W!X*?S9>$d2rh5eXrqkd=fo zvmz=hWRE0~nLQ&jGP8c?a({o{@BPnx-;d-o-q&@VbDi^gJr7r?eDS_Rznut=K>>!= z^MdH+HyybQYI#CTV6r?`zCi_Jfqwp@e-q=}?#ZO?RjXQ=#zjH;hWE(x4Tvv=y1h2$ zO+2{;K;ZzQ+5XP4xS%L%^}szf6Ru}qygC0UI5-w0D6oEKpnBATypB*NKzi`RtH%JF zpkiSm2kixlIiNEMhH5jI;UeLx`g_!%?oT7hE?9~FzzrPvRYE^D&uYTmY!@`gQy}*R zk;t!%c~ekzLS-(rzFroQs8B-&>fTL#7;c=iAE$5H^FI{6?uI9umr*HhVw?3QS@8ugKjACfw=#+*k5oLP3J* zg@qf%e?igNZ*I{GfSnzBY8uZH7?;l#Vh|^2@7MqFWxZunfYEvd|Yqo-NB2Pz{DKMJ6A*v z*63xNcX9ASmB#j8KX`j~)P(6?2L+RK*g|Dy=Y#KEMLkuff!g=`%5Lq>3`t^Sttd`k zzMA=9?5HpkHW_%#L7D?ql;PX>upfPP#=M_QUvUTKNrS9};wQg0Bt{ z50EM6w_+3c7!WHE!NH(L3ab6E^0!bHgsl+XMac$LP(=1)6mzTxVX~0pkdV_)s1rwm z{K)F)U=Iuc4NZUv!orLLA?hjpMfqw-mCgd7qW{%(!K5ZIw5Is*NY*K-AxXdXG$`Ff z9UjB`%z^?rfMN)Rg@w&sT;2_Ofdm>_Kvm;M8>vYU9|b>%iYv$~m*0-dsEWsVMMz#B zGKu+kcNQB`-_!sZ7kDDJQ1$@}vK5r)klq*cV@R|H$|gG(`EXu#PEIA;YB;y(FIwUv zQ5EQ3G2LAaNWK4?G?!<)gYTU=oK|$N3Nx~;+9Km5zF+?PR68yso4{opoC!~mcpf?} zQW1~o_>5!)lhEtTN$r4&w$yb>9--PwK7Le0cV*}3(g&IM9RRuuLnfhpiG+D z{N4Kk#8W`+&@Y|=`7l1zft#UOfO?_8FY~#7KLh9)NLU6cDZ>nh``}6`g&L~x675c= zkM7-=i&T1^UoY@9M~eD=9Qm$u1|NzkG|GxwhC%9~GM3Kfy)AYJr|1en&)?i&gmU6u zsqzMl%-MT?xBD0$8OJ5cM?>Mlke7w;9EBDqdjWq|jq$hSO9X&-z+e!137w=s{<-_~ znmA$F$lf`QJjuXJNd}hL)y2U1Tv~sJ+r~!z|FY$zJVY+rnZKsoTaY} z_|5;xxGUd30biTMT*ilP8x7sORI)uB(VR<$%IiXp2)I)oynrqOvb3U3EYca>4P!=0 zDxH)z2=`sV*agPG8MwF+l8`h(2@%zQQMVR9>=M2EWIYxtp+YX;12h_>sDwIoedpVS zrhWL0$Q8EocUEpW8B&fdP|dYY4c!`|WeH`zUd(o`PWD~bukga%2dZXiA&)A8nQNGr zS$rQvC^mBPM&ylO>R_INA_P!FLSYxvnE7wf#gq1H3MMr)j-mpiFO3|wlK4@}T#f+N z9p^XQ0~ZrQuk*|fZxC=l!i}V6YME<&Mfjc{f;SK)#y%NV_gSeys3*WcZS3=p`GwJ{ z58F3#{6}2cp*)I=KyQu9h>=_z0G7yAd=LJnz8mzM(&71|QjP28f*!C7EC2zj3QoC! z^V<0IzWk9%=?|c>CS=u0y0Y;cjv&xwbw9tZeDfr5k!g>r*1t^)Xq;(U6Y|*-dwbRU z=7lfj4$hB}PBg{}U%uXTIt{9B33e|o-$zz}k9=5^XTHK_L)IJ+expMO>SF{LWVwyk zx!M3)VhjdOzM@S@Bu$61#|-dJ769IZ3?a1{+HlQ=pMn0E%tBfG*Ez%n1bRXwoCv4^ z2Mm$I?y3psl<^yXBn0%SyqsK57#oN(!KCVtRMYw8OV5C}hX;;spWl=mi1sjnAjdo> zC4M$p9TGQx z>6m37caGKCD~t}U6b;V;-#j~$PrwbQfOW$4cGyMW#mMNCg5rLq>l8L*Q!1Zqt2rX>6b+?YwBmV}CE;LU!Vm`DfO zUT}2=!C5}F?bPIO_xp>BIM6l&LnJ@KZbD`9t^E9Fw;6mm9iOJBvx1Pn5jZ@kvj&tB zu+&}zD*|l%yGs=GLYR}fUju-1;MpQ$6~-LeAt<&$!0d)Y1e%73jRVJe3#i`#L>Z%g zRo>=x637std%o;cR3?+^7}KFa801m0HofL^xHHH7=$8Gl3s*eUl}`$gg~Q~7E#caq-sIk zeR*#-9uxT~?(~HmTU*;O=)127hi<7s!4NYE#|2k(w(}>D$3lJt9Z$XoR>eJbpgALl z9tKz<6O1p2iqe6p9CGO1psrs+O zG562w0@NmfwE-j>!1VMe#3a6Y6@75?;0sRn&2@z^) zP4A|9&~f*5XEW@0?7?A`yVkg>q9UDb!jTzZg>ZB~-Wv~w*iDE*z3O#Pm7D{^A*w_} z`u+Lnp99MFS_xwppG~hHB?4Z#%!4W+=cClzT@qT(4i-SHAYfHJsHrbP8-$nhOkiel zGX-O&L0ndrRcN%~aH*+@Nr!(|SQ^SKc&B@XOC>XTs$5sQYW0`4{;VsiJF_7Dm1r~1 z6$WV3#h?o+G!4L(GJFV?2|{vmW!Ukq|2FTg9Dz~;54n>dV{`=-)=Tp53qx5M78=^x z%DTFgpiDr*bLiiK#F7ZEH$t9vP_B~C&<~wBzMM10axq<_$ap?~Cx&sNdkdH?O_W+s8;vq`SfQk=6S&WD+MClpO9h=Y;xTF(100Tn~cGZ6QF2%<_3Oi5t`;Q|;TyDV@aYRx6Yngym^ zFSfYmL#sWZ-nb%eyZ9D+3YxlCKaW?YB}Z{TdJ;zx6vZo61Gb`qFeq(-fg1H~;WUHG zVCGl=&mT+hK4i*Xa&$# z)YR16Tb%f8`mr2#VN?_q3p~kwHn`q6-4X|7o`J#!lpU&lnNu3G{`eU%Z6xppZ*40t zaWgRSYnbVQ%He4n7{g#uS;4g)*^^vB^7PU1Cp9eDU`lMb#B~>*J#`8ngF@)V#*^|b z&DYKg^{EpSR`ZDt?Yul9H`-b{sAq~vlJCs4}~TpKFMLgE-iBqy-0>fyp; z&BUvqXfSfdNgxlvj8@OL#L4w^ z%mc3jevsMF_ZLXMB6N-jdU#bR$zx9+S^$ddieHoh7n8s(L-qa2j!FI;To~ILf(gn- z7t?QK&>;+V!?;dUI>v_-tzUK4^hq)KwN#|qEq6Y6L-bO*jE5xzz*#RSh9 zi;VrsCiI4c8ITkd-~9#HLa^kB%|3bD3001$BLUaIEOB-*)xVMwu^~nBQ)cOM{S`={q5waC<>M=0VQ{~GfLP_o`!8h!^5JGkY9a!T z_6Sh34VD8R9SJIm`qQPQ8Yh?##wc667c@xeN8rYY?6lG}2xJTpw%eFQ#)ap7&R!Tp*|0LV74?JlzEFs|M&;xxu-~BZfPrhHp?WL)q5e#Vtoa(4Q3V`*>#&z16 z6YHSC;ON(`mU4~jXlJy|&06Py<3|3$p8;EcJO_Z4tx=16My>>)}irrsI_I zTnqu(7~~^0oip(MVNIPvl};!!t;3ZiY3YDMTkIx3D3w4y4+1{L7D2Rs`JsVeh#ClS zUkgR9Q;V9w7dYgx!sVrG)@CLipMHM5RF5lm@<(Uel%?U3MJaUCs^Ri-xkjIp-oHNr zIML}K?HFExb~9kvkR5+upkMm-J2;|H1rVw}W&4rb&mr&RMLHSirjnzQ+7>-j8K3~$ z4A)KY#w#LzXFtO^Skd=q3k?1o`YPlMkNdb$zoj$beb&KUts8MzT2XTDpH%c{C5}zR z_!~~q@(;=nlux0+oZYr?g~9#_f)1cEA53z10*5C3+bk%y0=Co~&bJp5ZtPI0y(RF~ z*nI&PmW=^~k^k+7Hw?c$;$RSA2-@s)TqzGuBg@v`9?ByhxzJ+1@vFA|Le9?9q%<-v zT!P7n$>$3T{oz+^K(@h}wPTkj@X1TyrQ#xd4E(QuH#QK12>Hx>ekI%ffuiegO%pXZ zK;G!(8CY)p3O*622Z!EonRmBe2%+?ic;xk}*jEi-+=1^Y#YP)V1J=TL^Aj~@c6Q%9 z{1iKACKF&_^DHIWSX-Mz&m2|npm-DoRkTZ1$hzYBO1-FIsxk!~Jasc`lN+P9tVxGE zO6r%m384KD?Xc5(QLLLn2bUmS=bB5J6#Ny6Yc4co@a+Rj8alsaw{Jp`@b^Oy0*w;mA##o@M?g{(-l1be;qo99Y^6r-jXFxdX&x4cGNe|zF+y}KUD+_? zm_HDw+JW~$zc62TE#PnS)Sr)KLCH0XO-d2rc@_l~5s!iU0geI$kN^Rc^#4fypb&yo zZQvS!kbgq*a010$_Xk@YFfC`ovPV7-7%frc?Fh;?P#BMeUx&lr;C{zP(saFVoxD!O zaL0Wi?Uaz;l;>5YZdmrMi7li)|5gNiAyEQJ#najmk^<_()NY0sdM20hCPv~R#baM| z%9P`ZXM^H2x6Z3(pv`P`!261Vl1@F8xU1Jtp)BXZh1)gZAPr@>ybT2pG#w?=o-;(& zNRCb4{qM8X_^{b!lTnRt^|gH>qZ;28EtQROVpg{L8E)LV*{IJJmSYlKBB}FRi(JwQ z6lQ@rCbtdV?W$B4bstR(_K+8irg{#aF6h&}D^l|8tK{Q}koUyIrJJfe52M0cqdsZ| zdv;n2ldkWeD$i59pQG(m)Kq#3y71&6ETXO;L~}Q|X`9qNWQIa05(6N3DMauEtyMcK zt6=b6Ufvu6`E*`>2|R*VU{HvSO9@Lzy}VMV9>qZOa$h#_Ik}-PzQNt(@amMO7D<+| z1^QVfk1fB@$vQ`@Zrs6t%>0(Pn$+<8SNf}VApd_L?i<+k8RTac+r^ng4BzI2o%9>! zoq6uEwQa;6!ue`6Pe1YcQ#e!3dy_BZek7M9Zr(K7%*fl}pL2&_^wdwXN^0Lmp9tM% z;KuvZc?_HUxk(&2p6#`3wM1wgM#=?126Lhf*7T-_FnfN!$wY8)iKs+s znVQ_JHag*{BZuugE9Zr?J0ywi`b!fZr3(xQKhb7_i4UUeO`*0&PB4*E3z zfY#Cak1GVETw))IVyO4;+#^;7^J1D+YGrI-=cC3jE@^v*cVtzXk#0^0CDVOMsvNRs z&0H>1F$IEIW75*e1~t?3g7S8NESvc_{S8-a;Tq@~HR_*#C=YB_9 zZ<+(_zypnH<5Gax~E-G;0nf<{c?osWiOg7FZ*|G zO_m*Zx!+zQ&%jkTe=^UgNsTMCRzCXmH@+RRBQc&SQ9okHRA~$Pedn;TpZxPyc%^T+ znWdk&7eH~#q1I|PpLhGy zDcEM*9{i$_+PBjG@#nyWfNZxazy-(g9~S=S1(%c{?Mo)AvZq7_{x_ zm<*J@1V6~99ndG5-S&DWJ*=>59wQ)TO#P^)UWOvxpFgk8*qk>gB-_ST`|_=+!9n~hbJM@J zFeAUrj9S}sU>q_iYU6;;3}HM619&Dcf=mdMPQNldk!#dm0(|;{28&!IM~BDeb#Ls( zRK2kq&!_BIu7s+FyEBc*qL-rkO#0MeyW?9=-&hd=Z`{P0p}Om2udzyW+bt|6TWi@LK5*2n zTAceuH(6cg7n^Jr%^gXcQNXzZsznM90ugZ}H3QoI4yO?G9oZJ2iYM})jyd6^D zNYpd6jq|Pm#1{^@K7v58di&v?%}M>kTTxp6=n-}WI?X7V{7JR%nJ2a#qAiW%UxnvI2HezqBiv z*Rm@lv*g1GnJ9uuSvt7E@e(|A?PTI;#t|T=JzDvPHRX3Bj z7)C}c?<8$#$ph=bz$}@g=h^e;_!t-`lzv_t;yz~x_c$Lz@gLc^^x0#XGoy?O*!QG& z1Kik2Z(V3hSYlH1zD6mzhnMEBo=;0Cc=))pxbk|CIvzn8@$JxmPh_+;)mmTl?YkzZ zn!DKIgl6H)MtkW>ERY-Wtoo;0e(;`#UKt z#@v?Q*>I1HZira3)#j-@Y93&5^*TQ&nLtfs(Cyz_I$$Tny-vPt2+zMysh0d`#ozp$ zzMRFI?rnbEN(p(E$->?l*(tl2z<_uneZH?!$(Qp0@3AqFopy(Ks4IrJ+-r89YGR$3QsM9W`h9;RhaBa>&w#zOaX zgE`zKQ}K72YRRNAn4KM#$4f&~&)T!OqCrs{&Oi+>2CyVNxY9zO+5!y7B_BQn7Zy&H zJ|8Sj4Dk1W(6q2wr1)Tcr%Js-=H4FlTb3K2-tpWwr?`JEH*<=(Qtv3Fn0 zno5E<)mHk_B^t1Fz99?+*JH=^f|B!>iOtSRhkBgAkUrUy#Mki6omqIK z0SPU)0q?9yWb5xbZ3}yS<1(h+(J`{^F=@lErFmYiEZccvJ?z3A^1qJc?c8vBdSs+IFe$KJOmO17Z@PmH}cX-;XLEo?J|nO0qh>-A^Ul z&rY3B+tu{%+xEAW#d=uv zc+7hRDdVd>>z4)DbR~w*Hn!IS4Cy$F|OPloBV6HFGnCUium|3wq(+~tD6S{K}Uc5 zy;-&CPmHl+g3HjZ4IZMTr_Z}S4l@cc^>Ckf%5a)WPfK_H7yFW z7N*H7Y5|9|qCp(E4xjq#GcqzlmxC2*{m6BAu+LU4kpQ?*pz_OsMGYEsLsR{g)3Y6L z+tgwrqUxu$v1dRV&bn`bC6>l0_%FAl3ltVrYKCgB8L9d176^l;Sd=`x> zuB1d?A7U`70-ZhfV}s9b-n^1+xbpn}gERhfKrE-B``oP;*($+Eb#%k{RMf@isvJDY zYcT%?7Xz?SMi~kAO!baB&s;mz<}X6C%5C;LW&kE(q{xbx0!w^ENFl9vpXZb8S=X;Jl&%(QfXgN6SzZzhU-dD7}6BHyY zgP!T8Q$N$8pXXyIRYuigQ$1UUK}V7IrIr;g1sC;6JS46TGoF{;!Qo-JI(2zj&&j3n zlPYg)J$PIZfg{|W0(KC1SVKUCehLQ8@F6i#8Vea1_5OnogJ0ndfVlDuUN*{Go7Ft( z>=v?q{3@7NKq2?CAy*r@t*s+eWT$ax1f18QW%tRu@8|qo+iNoyX(Y+~6@g_Lt|PlT zXLqCG?%p$_vZQ2~^B3jc{6B~zIQ*a@nOSuB zTfaK+7H$hyl`X2%w|1(mx;ilo#zAB7F7J$bxOxft*ZQz$ zE^>v)%00jqmWQQ1dvnD-@Ud#=H;WNA`t32P$&E1)MUzhj*H$eYqRyl}x|ro56sD|O zWHBg;>u#%(x0)-L*QYA&O6yFcoGir;t?7a=%^z7)&}@d0PWar990`ZAg7Um5?aAD; zET6dvlfnU%DYSk&-;;COi)VT0)Wa*%4R1`*>QP^Vh4;bK+OUB=vsTOKIS zb*V#?(y)1KYsAg_&!n2rIbcJROH)z&nK_%<8<)@ikvYJyk2o^qfI;~QA+ah8#>q37@X)?tSd zzgFuwML=`nz^H`S*RLs_1SI*KV=p*g(8+I|Ru%X(e*PKVDRP*7WpAoc_oifNfNK5d z+n=o{MaN3|h|%3@rO^EPv7;B(e#|znPn+IDU(6HVz*R}N{K+#=DYtwm zEg{lH{`<^Y5y57GIw$o3O@PV;aD^mEkS)E_60r^q&0<|=>AW(k$wUVyLq9iHa@G_G z3=l{T3By1|DOTy0m249`B=xUHV=ITyWMvc!O0UCeCJTP&JQjE7;pREK%^`eE4R9QAivxvi@& zddrxhrx+O8X#Hf9(vT0dLAe=+v6b%37}l+NJrUoHi>luiyO;>T*E(RtF+P?v z&LCql?Nb(Pf~q_U5lrlIThDp<2x4{W2=#QqGxX5u6w}$?cc=4U=jMGD`f~jqB8;Ec z)=>0J;5|o^oZEE{GMJn5?5xCzIRj<(KcPR{U53M7bERxQ3(Rwo*G~7mOBQd%{rCH0 za&Hl{ybGtLS5o-)mc{S-@cGY(E8C}x|`Z) zxbJB9NrISwfsK_frwvEXKY`JMdGOcg!m;gr-}C$_`!9sxNMJ9xAhGb`M6JJ3JSO|= z%ZGN-D`5wW73#2b-iMgxdcl^!DrXL6`|x8W$n}EsX_;41_1?LH(?-hAwSP{md%Ip- zXBmF}!p4j}R<<=_HZQ1j!+}`i_)dE@0|6vMJFE`M|Gc7S+}{$FSXWO@4C`UL%pV^wc56@bg=f%PmbZ-T z`j25K`pOPqTY38X2u>Oi9BxnG>3qA#IvUb0lvtDTbKmS7&Aqr>?G9q3)2X*_LWnGDFAG$yrs`_l*2qKa@9zHe09pvy=}Y+vp`Jyr!nEHw8(u1{c)6eVdd>7uWY% z#?ciypZ)g_R@qgy+-F}e=IMn0{||iBm6}#mx0$M*(+FY?q+Ld141Az?{y)aN0|0Y^ ziH8U}w$Y&G>A&X#*cW1I>R1#FGA!DoKH(<_uf0{w2-MmaD_Mh;kT#+)WoNc{5uAm= zrRzS~H)+~!j<=luc$GBe8GS3A=@8xAc6ur~HjK0}y6x8e4^0)fqW*Rt4J#@%d>d)6 zqQy%|H-aO@Q8<}42rIP`ZlHPP!e6dq&^Tb^oF{;^BV}U`1KHV37RZVaF8LeN};il(ZS8&on z03N-SJ0MSnS!b>gq>z{d0dtyB;%J=x=RXV6IimDKQ~&S{zalB<+tgp;HA%A=EW$x+%rga}+R@X$caTm`$&c>NR|b`0e?zf`ACF0Dx~3XlpM5rq#A9Whr^22$P?%SNGR7o8 z0>H9|nBho_dwUPmYDGmViv23rbioIJF7)i`j%cy!pHxiO$?abVH+8=P=rJ8ph!E4V=qd^Z?YR4c8g?0zwVTf5j`>;BrjPSWpVc#QE%CvlbQeXbNz$wzO=YkTdMQQMSvOc)27~hmG?%1 zZ1qVsqc^s_Xu_AB*!#aO!x|9synzp5fm8{9rE2EUg7YW$#+CZ~}tOI3uKRz#OLP2lU3 z{3T|-o<;Rc-7`$DbM?om*RLf=*f1Vdq%^^|rbCNYx=Y>mo>y{Y7U)SxHh#WEV4W&K zhHkOuMJFUGv=I~)05XAa{xF(@0{T(?0T*gguK@z5VegHu~qWU*1O)A=s7{afaOL>N+|I9EXHq1Ox|B?FI9aFEYI%PFk14u3 zf9!a_H|t`J&0pfPG2!;nl-Sp8Ycot=4cIK}`e>(E{}=O%FYX1}!i&HF>p{4l^z*@m zXSQuj(Ye!ewxyX({FfB*G!3sOe^o6re~MVXYjyQa$=!8F+|SbfR77Wf`py+L3SNS2 z?kf!KmY`UPk)C*mhskE%C0W&p4(>Avz+-?YocZI$0KSltr6E;mnXjd zcj08#2W~g09 zJO-uei>v;=IaX{1>WVF zUI5jCpMSG1u?!~njZ{EH(}aN?)dI1q=F@li^Ma@u$cSmy*mmqq^yJxoNo`oV$i|!L z7H`8%98#V}s6xCwcqupa>(SEMgBuw{Y@F_Am@*D;hGw_3DxZ%dyTYwx$VNq2v1NrjX_5Q z$T1~vbk=o&KZ|Ui2F0HMu8piYzV4H%q%5b5)CR1~#_PCr=U{-cl(%s^sT}Pu`Re+eMEq+`uT+ zGBDJMKuUI)tDE2aX+`CKRPSBqP+IQ*0-1~&@!hf(kn$iMEtJoVW>arh6 zo%`@0Du?=p$lbRo26n@^`k|S9T>k@Z{zx@ieh@VNO}1Pg&*2iDzQ^AZ-eqB<)K7tx zeWbg9YP%tODb0ln$0Ut%XZW6o=lcuClHqwq?kq>B5b8C1)<-ji@Jvz~3UTgvgYk0D zIYMLsn)&waE?dM?jl|f{8VAmJVmI9}9X@ZrzpX4Kn|#{5BEdEuXQ7hgAY^HJnr3Zk zzC`0j02}chZ=%<6e}V{8WK7e;yIpf}ep65Kom3nF&J+!Zvx=nj-1XZc-zw@c$IEeXaDahB0TclsL_o#{5Tk(( z&1Djx&IKFm8Z`9ucCG=Aj{E@IiH(cXx0-+RGWR@Bpzx7cf}40ojT=cQ{7aiRKwoar zj^e`|t#rFZve0+poiD|L`nK!ljQ(QH&d*`rpG(Qj;Fs+G$Q$}32k#Z>MpJ@$fXI=z zyUNv=h{X$c2e<3%A|5}wYT^*nsE-315g5I*Qg>GyjPeGiWD=-Sc zYjDwdRAP6J&R-}pGO`gV#vr?#f?B9D5(`9Nws&&mmXBpo^TR zG!f5yB!~VjMxG-r-WQ@F93^vYW z&9uv(PgMwjGZwx;3L6LyPs^xVz!Pv0FfD2RpoK-a6hJBefqY~J*gyrPipZ=;wt8Xp zx9+u{=L#4y^x0&N%DuM2YFaW3}~pXyRIr z>w{LBh2KktQ9YD;rkV6taMzEy7&e;N%`YU%P0iZ<7c|5!eJP+Rv*3(`k4_Eh?@Prd zRhEmo?I9t?!H-5FhWBVUid)$(2(o0RivNOY+>LS`9J!j zhZ=^knjuD%-4)hC`flU#np!RNm(b2epdwe%RC^&8Fazq86;lukAd5rz3~JbnmEFR^ z*>RtOP*- z2-+upFP4jcgOLFlTTwHAbo7VI5%2yKn~cm^sJpa)E(TFM0Lv+xqt7NROntl)a2y7O z896XJfgFC&A?4Mp;={U)iJ$*K9QAR3&i#HAhiu+PLjLb#PP(JCTXKo7ZhmgnC*rTE zJ<{Oa6%SUr<^p7Od$9$Ejg|-@eo5-ckM7%jteMBQYoD7kD+go(11d^3qXW}L7>OhC zs!JUVM4r>$ddlT%f6x*3=t}i$rJg1o zovkm26lonicdZozvfdLkjZFU3s?JFGdXY;6_Zs`~%xBgBpND^<6nm)ktS0GMh(|C1 z32(9i(pSC`H1YO)Tr)e&*O{MTQ=34aK{T1Qp;;Lc!Gwf_NjTvU*08KP$z*T)`LFUi z#{$wt{RETR+hTarKMWI0sviGHsR*<`qA~RSbeyQkGjiDQSxYFPSkRNHY%3d74uh3X zZZM~WU&}v(!T2$9I)4vf&@ON7y}ZqllkP`iCUNZfSv4!PSNe&4zsr=g$5gx0Kscf7 zuQztt{?*i&bXRx9eZwbg6;3aDJ5gn7RaH0>hK1vEa&kheDTj9K8r?_9`|abU6iRMH zLJ4_n6&1nPa<^U2UZoUUO^n&j6AU&Y*(zi7E-G{-TU-*y3eGTIp%c`e-Xz!;YjCxr z!Djs4TJg@$%Z=z^Ox@FdTbfdtJ>}3;jR>PKG9tAX!&S9Cdksa{Z|OOyMCn$8_ooJ8 z>g0I@LO+)fRuRNBSlJyHFhavsFP>d3GXvmHh@GpiANS=q2?v=%KK@+|BR1PXEJCRn zQ(5BgsVc4C1M2;61nho$Wt0_4jaSREm|&1#^2xZoXm+kp*Q^>#?s{z+B}f%)&e07& z4!_3gSX0Xy|HXcDOO&>SK|qX)!{S?&qGRnt+7OrE;0sTiOqAyx11de+)ygap$*T$2J~D2QdUarrc$A8+#C84Cl2nmL%n23Bvo zZ@DdoWEccHxCkifnOG)zGD|;Z->to_MV>@Qil6Z2&2gf^u7hc4;UJrV2b+9yk-+jT zg6C02yL|cS8-If|1>da?_UWc+f0M0v7a)A|)beWNd8&*TOjMCVXD{%nC@bP7aG18u zNs=_r448eU2oq~Cijphrwb;qHvF@Wr(%Y$Xhu$}b;D1zf*f_ptWMUnEV1`Kzul zHw;;S5Sj6&3(So2Jk1pGGXVLiqwN4A|xUDvuP- zp@w}t?MqDV^vGi5$5~;eUR7RtQtaq4RfRQ-Ojges(}S9xuCL6eFL_`vR*8g8Vxts6oDl3dZqZRZ(T}V#WogSjz;M)G8ONrY&{wcV>l<0IDgwSv zdRl6X4UO7oO1-8@N&D~@cI{&Uv=2YL5s75>hrQ7*ym;^q(fZ#MSEu_WFxS>Kz9coI z#{H{?pG;aVaJZ_@8&ZE&B3M^OKS_MmCP_7f`|w931Q%P1bwh%|!zMO=^AnKLv^EMKe2y z9{^CKm$|&4m$BsVhp47m1=&}fw0F8!IWw32Xcm4w8pPkMud0ffI{14@D8O8Y>r8lF z&mX2M1_#GA=Pq{NnNy`lV5I)jaZl||ra6N{RWnVh-}*ju>VTuK{s2sK09P$d%yRI( z{FtJCr9Yt0PEIvhA8Tg{XGt*6x80o5byJyPpqb0kX;f+SJTDf;ACtiXd;AWa8TiASFgQ{66Ip$fJ|AiXv ztC-~X&N8>{Jf$4RJoGOg?QHV!&}wC#RLEULEhj;)vNfJ*H$zo{~WQWU2Ky!Sq)x~{J_ z!lcA^(N=yzIe_mx+8Q7JQi9R@dgbOybj@KK26OY4*Wmq3Il=gb!I3d&R{#@`LXDaX z=Es}>*DRXwPYll;;?ARln1#OlczFFE@cJKLT%x%eb2TJ&!Jp@H5=J3{!QD?Hm15A%6 zHZbfH`C_2^7=wtWI+xc&%9;v2EG%qB0$SSIG?>(Svj-MSwx&hjMr+modUD+&*MQyX z=vv8PTk16T@l)l%LJ}?|K)&jDU%C#DT8`57_r$xb&3{^$z`;tYKYkl}5m^w!*z;B6 z?1bn^kqI_4D~k}tRj@Hk(Qi|5E~WxAwQNAba_yyda1|UU#b?%@7)`72%ZNI zkAycgM3v_PuKvTcdge()f#<7$+oM+-QVwjCa>(u{Ldr2is|w5QjVc68m~V*q3NVPJ z#QF<$s)QRhw6Za}<$_J1^ocq?n3stX{vtbYju1O~qB2ypG-6kDm0g(liPpg1k_v@i zX15GMJHAp$@%zc9O2pRUl2X+QsBIp-)5vY0Z?gl9Cb{kq6!|G#d=C8I3(X zdL7RenN&pjY8k8c=ywl|VGm!rxtjOBp_07lV%0UC$7SRp>iH$^A&QDo-@moUu=|a$ zU;_P9%s=jJ97_FGp~OwF|2SP*)PXN7NQhd9E+;Pqa+8ILy1G6K-E%W*)1*b?=J&dk z+1kRzGW#n0?0k*BUUMIr ztWV!}en(c-R@YDT-;C6a2!F}(y+@%%QDOBc%kHWJZ$ej;pE?Jep=aVBL_K~J?jX{q znXet@lvHe?<;X6^WfE$fTd2!DG)AX(;rlF;DLZ?_nUBUCnkgjy53w-mdv$Lzf9}Sf zS_yVInN#4x)FZn!a8xK>vbcwhmw$y8m- zdtv8VqG##2KY+gD{SjO@X?6BYU(LBCviWvx9*Hv~>5c|CCzmNL0va-o2v|sHc^b8G zO=}K2jZSb8j+_nX&g)v}rqm>K2qtJ6o($S9nrsFYxgy!)lM{jMc<8*{r4JS}b8Kz~ z@NlfjjC4c@9|Zz?9i|LUf%S$*Q1CH`4}iA~6q#rsvk5NrsoKybO;()5{&1Wx;!z!5 zoThCnjg~&rlHv1A24cJZi#P-1U-i1qwZ|yWeGNE$z?I#OSI}zi9 z@~sk|gYLY~y7`X$?oO~*K6x8yh}e}GqsgD(_2typ*Y6x4AtbB^=Xe-MyX#vYpJiBl z$x6sGU^Lp2A;sjFV8|6ptRXt|;#o+4X~dA5bSqa(z2X^yBRO1*_ptMR#b< z6kQ$_;$)|VWzaJ|^1|CxLVq< zg}S1j;=g^@m!wpB=ic1&2z8iXLD?k4Uz;Q)YBDi(PGz!H=X;v)u(!et$CIzWy@7>6 z5FwWPvs8Z@coKb18^NF$)iS0gG9=ZS*(Vrc? zWLJ!Lr!DW0vk-$H6@uS7n1vLto6kc5N;2h)rq43Tbj z&w%fuqJxgO$(Ihgn5?q_lug69XZovNrjZ1k<{sL(O9OW`4nk}}nM-FEXBjb=ccnW` zF54m}ktOq!b)w2wK)qrDfjDg6{~cKA_9|PwV`}&q$Y5_|XorU}qS=;OFEqJ=ZTl$K zCHw~E==!<1L@9xk0;hLZuB}mkSP&Lp z_M10oaQNZVU?HeyUbIc^NBW0>4U%)hBNSs<9FT zh<{&wAX{`Aci{J?gxhwU&uEs%EuKW;C(VfXyVJ!Rfv7)L@GKE36?q_md!@o3|HXQD zUYH8v1?r#w+nJ}?__y4$&(1eCjyl?@Z6_JSth%teplPT=wNcrp=i2YuN_zuz2A76Q z;pw6k6yE>*H~@G&ZohwUBA_gq0awXmeLt?Dr-uq0QuV;%iUKRB6%5TDAdW)c#S4E| z4T+F4x|k2;%W*=ecT}KZWSRq$k|==5P7X{jW;lQ{Gufd`Q4iA$;0>0s$RnrU)@$27 zUD~!Jt^4hufBbeVN~dm{%V4IG@hnSW1e0Zjew7Tz2)Qg*d;_T8w6#T#1CfA=ECzBu zRS(ags~65bQht(}M$qlpn8#q&rkvj#9yGSdDi442J#Ha41FoDba1W-Zr(Nf}m}yU+ zZUWCGw9S!U^u`T(6){MYMdklD5;$XHtewWl-npG)jrUN*hFXD@Wtn2IQDF;ISJuI{ z4j`l!$6A=wg%oiwF4>YhI#zbGqMw_F6KVDB2q}sLXLv3%Qp^`K=bRh?{OvFkyU6GX zsE-he0fXiS19__f&`z*?p}eH3rG=&{pwUqXgaOADqRJxuLM-ijgNLzAbYYD4odI+jZoXDm?rJDjF@-Pz~60Q zU=zDZE~ZZV`BKHei6*?`(#gG4<{`t&PX4@_%Y|d(aGf~RF7{a-=_GU4u7d_af%w~v z;u;uQh^a@;*deQ!WagZ7177rB7BDypcw2nejnCC^X~6^1v$}XzE^NBMxc%fpl+kqpJye5nx(SnD6B2MS^jCcEl`SR>^0}aiQc+=W z!Dwr5n0DLVuBE)Ec?vzBQ*e|I{rriA0oYxkp$s>7TOJzzDk{j*L?F98NT_0}&9V{w zs=(`3vL-%(->vl)DlfFSo)}V-0VWR{^8hG<{RPKUVP_f2VE1RXD*ax>9^Cg0Z-XrU zdUX+GTsm5eoSz*HM{|Hxkax8dW@Lb?I82kPsbb>c6PUzq4&)wHZSBWKg?PHHx?9(h_cvVDQd(LJz~9)m4#+fqY=B% zfq07Z?8~3;lz`JKg*+ARi(9Y$fx(^;HF!SEA@U0VqqLp_ri~deDNVprVkN`|oZdQk zW`Vyl`^pkK5K&cOc^_Oc1BH2B1~o2L(rZ)9Rv9fOa2}^=>bTE}S_R#Ub~@}M@G+_y z8iNFyjDigp0N3&%zfa4k)8-<{!hFF*|;ka)8==1TB-@%gEvGA>{Fu-zoxU8^o z5B|jJN46SW96P|2kurhBcOk8e9!)d-{oFA~yr*%)!-PxcmPod+&HI+y8&~6h&4@C8MkmS(TAW z8Br-wMpi16kWoYkk(DG#u7)y_6-p7wOlB%&M_DB+qs++tJlN5#kpwG>mb7iTv{Dy6tVxpQm|qDB<0 zCvdA5ip20mD1!H zlpCABUwy#B5v%n(b}S3zw}A$Zfff8&-OgB~i}DhWby%srf(#2pH_L9UKeSJb!&OB9 z?C5Z8rQ>L;P6G=x2Hm>$LtkQHz5&sj5a@b`eN=>l)YkT*3w&4?Y7P#M@@#A{q@WC5 zIBK3ZIgi={S?#&VSV4Cef1zTI_XB#~qsn)+*c45S(F=H$@uS%TKa$Y)q$YDYW(7py zoJjmZj^shjEC8vmkr0r)8?peVQ+Mv%VNp_g&fG@~NNnE=66IB*tw<#HC}raks=ze2I+r-*ZSDvC6UT7?rfD= z_0>Q!x}^`VAoa}TxuXE<5_;k_ziL&CN*J<^l+^Ldypn(5RPgbAduE<p%=Bg3L2^@rL9~ zf%tK0J~yTSzoB6-sfKz!);tddL+^gJB~obKX=+A4sX2a)B5DU+L>W_GKhlNTxcbj( ze}rNpV1q=U>BWv5Q#NGMlplb7i|Fa&KlbCSyacuhRr2`QE8?+}C?JPBLIN3lRgfML zG!}F#mCt@-#ov2Q{RpRLWN-hNd&iu(WLLfXRD4=%HlmoKZv4n(viP&ESJAh4Buz(Q z`o!y%W7A(gCLTSRp6z)T7ifLEk&BH2gE9!ptH+_w1Fhwk^|MUK+K5^ofnaz|I>5nj z?%wQ19@gnE3x9$Ew(goc-}CwbG13HwhbB9UNo-&)^sPMLv_RURklCR84yKk9*eC3B zpN8!-`Wwo>^^Y@O8*MRX@(uT9^%yqEn^^EtNzzTO?Eh(8-jmst0nOJ(Ylt7ftrLx! zuR4&^>FaYMMnLRxnYOlem36jCKj26#kmVtj9k@-LTwDh;ieRxy+~(Cdh#7j=@aEFL zhJ*+IObMKs;$K85pJUyXe&F*pWjs3Jw9K}Mg<@^Am=PD9p>O=N2)-~~{rx6A=a681 zhj@_yZV#T|*cD!3FKA_d zu$61CyD{Y#_fg2J&nFZq8%%>6K4}Jk--KfC0eb-6P*m)>ei;sBq>4@3Iau3I`Qru_ z5%CP{6R4=}5g^vEjr40o80pWfrgld8@C7FK{4L+!q&}w|Yzb%5PTX_uuU|n4QhE8O zWiMZDl#n#i;9xivK;Bfu$b@wqllkXbO5~1&oY!Jg`}5?``_`7D&kqQWepIANij%cV z-x{rRaV4LP6lTyO{=nNoK8MJ+hIRaD-#))T@r_pFaT=DP5HK?{yK(d8aZJL|he$WN z0}b%!R};x64AK;fd3CmdgF9nxKh6C-g==Fe^Kw3jqg@8p=U*AObR)i7j~q&qE0jkz zZrH#|A@7iN5yiW+g~=3Cs~*+`9wB;^-u>%6*oUNj9FfHf_nX+rR{X3#_8}xE#{s=Rw-q=GI2H8%rn8rencT+N{w`>6G&@*oCI=11mVYM~9I$mYK<=ut)i-&b8jawVL!G_j#GH7hA@ zy37Vb__z~?Pto?Mu+Ebw`rQ}1w$oAlQoaG|$@HY|TU_}#_h1mYh|Ul)fVDdcmwQvj znRvHrYs(m?{O7XPFF((CU}sE>EbV!h)0kQchcgRbsP}g*p^#u$YmdYzk~jKGzu}d+ zb#Q3}XFz$+qYN*>?`(fRA*M3G-uNS8@2OWHw|L_kktK`9vm-XQUD+z3d_lL(59|B3 z@}89e4p4NVocU{ z35A;Speqr)Vf*JuR%LZdZMRNVGg(?sUJ8n>ynSUWmlo2chG)QQkTebz>Fcn+zYu=q zP@i1NoOdld1D%Q4)NVeqke7=Ak&FWoshS6L)|4+`r-j&QqDVjVTW~B%uSzJYwrh*s z~EvhN(mvFJZ}E$CE5m@$;{H9@Q1_e(nB%+c*&|8X5#GTC35{QivPAhKQ%+ z4T#aNB}Pb4yI&VVR>;66l*p^Z8OLnwl%yi`170r}TNa0Qv&1}F{rAfEBy>UKRZ~}& z9TNQk7|R$+$R1Ed3D(*M4S!HTktUR)_6d_Ui{2loa!z`#vti4=ZExFN*4`E(kNp&x zIS#eZmCPx|Ac#=2>R#v*P^GhQymzS{OG`ljKmFmu2Z{nDj?z0; zl^7PrLdE6fwWPU_ckAi;kS=xO-VgkHvi9WFd0Hacuo*KYbQKvWv>9)0IIw^JVhT={ zrqW_$&zf8BUAOJWS_vFOpXk+`A7fX%oxQ$IQ^-eIWDXr z&*Lf&LshNn4_l{`34&rq-T0Ld-W?d{ffmk4Yaf7~sAYpmQi<=MpWq7uHt0Cff}cw9 zITT|z`gIv!dCUs~tK7`+7)Eo?`T0&^d#P1{gf?%FPe^lEheAIn-!K>r$$RbwU%dL{ z18bqbdki=UkMHrp55Qc-^Vw4PS+FRrd&}&|E+LlVZ`GoF3S>0!%b>R2P z0LN)9Wa&6Z-$RiYo{oD~AB<&Tk@^Ie1JxtlyYb_^YXa1EI{L(W41Mcn$Tv6I!_xV7 zb~rERmpJ&8uU8KnOZ&H1Jk;&M=d(+@agY&tSOf&kO*4uyWfUTmR5K$vestQ`D~^>-;~gl(1q8gOi8@of3jS2|IX0PXfpDX z5l{b+@TmkLq;&-J4du&gv|>p~Q%mdkcyBdCoF04lLYZs1xGuLg7p!?6s*msFV0a0q zA)?ned8y6pzQ=>rAQ<3&1zqE9J`QUK+W&v;k{+1JVJcLgCXClit zJ}$20`y4c1$7Tn!dKTc3Nfi0QCTGwb5D^lh!}tK7M8Tx5PU%ZI3KooZ+j69cnjDf2 zVwFzB2mg_`B3`pqU$1X)BHhZIUK#{MLO1Wq_4MXrYq=z7`A&F*sIT-0eg`$(E;The z&tKm!0;Ro#yaeRr10YM97#BxmnO~#ZE8TPwj+?5p-$jVg6#AD+CgNMS@}Z-_K#}@} zOe3NG7_{ThG(E6QyWl6{+*`#7;l2;Y$zF1uY3I1zOROn!zupf{ zkR?cr!iha4&!!Ws_k3ng@$c~AUoN)K4`&D@E}bI(FsPM4SaB(ggGruvV(@i&`Ofsl z1Z4lD4}GNuYwGdQ(nVAWw4yg~L7APi@hu zoYDnC0CNe7BAsIEjXj15HXB!yj&Y*r)Br4@U%~u@7#SG7NDM^pjRKtT_69JF==E~h zl68&emnq;?thaau(Cuk<3Y9G^)f>)rocB`e>SdzjQgkAp{&uzq#5*-u?|Jclr?d0p z>YqYFLSpZl$^7St*RaAq2p@nelJ?ATCO0ZHG_)nhK1Tmm>CqBA0@7}+M!^{N8pow( zlhE_0fxqAXCWm=}(3mcP;Duim4O4?GPPwhFYEw%SNrM{Gmjk6h98&D#UsD2&D^h%)*eOfO54VjP_i{4#4^ym4G_* z6vK<*l$`KTH%mz{`{S2o>&DNV)lW(J^K|+7k<60WVao5rpc2uXanLX`*#_V4gxR-n zw$#m(stySvGDN6qh{ww>&qDoMc$~2JE+6+AL+kPw-khi|bPf+&WGE7oHf&bZo3s`( zm^GyCCDsz?T<&QWXzb*4SO7+#Q0E7BC$H>&w-|6DMqvc=N)eh`oV><3=LFk?lN_w=_r zPViRoLl2N2{a3Ps-b2z;GHV77R>#r(B*L2K&gCk)H_;AN2T7j* zC=^e(5oyWet!S-*>qgwY5*5kAgAd*^RAU${r0QCU<7IxjdT#HQeHc=CY;~@gyMa3Iyeva*LgtIc(6k#{ITFbuTd} z1i=4~E)s>&bf3Pd(puaNj}@vlU(qky<_Cd>M5HY%YDfsLsE|q?ZKZJZ=E(du6Exty}s}9-3zP4&9(M?aQhpYTZn`rGQC|K z481G3LuGX~D1Cje!ZUf=EEGcv_l}e@krI0Bkb(Np*43E*@5JL}c_dEm`i{MaKDvxq%7#@6-QVDMsB)54 zUp0&ZHX{V%ABuyUn}L#$lvKwEFo<>i9lq8A_tuq%nzPQnK8a6|y~fPfQrT^o2ws)ZWCl8cZdRFpN;|*E{T?<-Il{aCd+)wU35v?ka@p9Iy82E zI7=bfxrk!tSEeYk3y7i=)V(O*2x}%Lrfb66$Qmg#_dItoJVv|q;*p3Uf#0G8(3BF7 z5=5`)Q#q0txeK$d#Cp&JP0`om5Pzkk(Dszv<$~UqZf;HYOZ{tqUe34jRz1VyjjR1??(Z~6*40??A7x*V zwv(LTx{0eE{GDG|ne)HikHwT(=RloCfB0sg&;k-Wm7+eXV4I`~;}lHWT+xexE~iP5 zx{S87&e}1nC#ir^_`khyKt@Wf-Eh# z$CxQWuUbdtZA$5HXGtybU9{D$thSb= z!hX6Nd4rUqpCP1^i`lWiq7I*HZ+E*^bz9;^bh z?PQ70?nMve|4+o;BC~|7n8!=8H5YsYlA19UdRY;1=}Ym$y+k@je_jxq6FD}u69Ydg zE+vI&Y;24SYp6IFw^4pb4|Qk=BmE?F54UNvaYmdyx5h8tK;#(hkp_!~E&K?FKFGCP?Nl>u1T2m8N!D9=_C0{};xS;&V;39(rR;l(KUt zMIFVkgZQ9%dwWxJ+ph}=3htyu&5*PQ80UV9E5hM*(}EVAKp%pa%9qo};k1QRRfSCL zw}JR|d((BS z&hT^rOkfE-IO8rsEb%b$BsKWIw=Rctu3gG(sGT&#U!-o3hF#1`vmz8*i8t0X0} z^Uf@su;ebkk+I`z+w-l9R&5g}vBkjnS!@38yJO~o{an$J>sK&YJW1K}h;~JvVK`(L zi?}OnSG6rM2rbB$iOF$wZ-T;Of=Q*IZ&7vivt^izD8EDj$Rh*p7`^m!fqh)JHGxOpm5hd8OoADC zJBH1s0zO|n4||__rjn|$h^pc~<8hUr%wWH;s2T4#j%%uwD0Jhyb;mtndc4szN9jh2 zG{645X!YbxRLNisNB~C=h%S?QWHbq$K(W<644*R90!K%QZ|&N&@BquJ!r( zrEfFGuUt3H2ohw157hI+tnF@Ogf#h|^^xe6s>Ag&nxciX7d_g~^{cn!j7AZaqO>bT zc|3t9dup$<$oJuNFg$hP(@h^|)@8e}op!QmH7X=oh$AO#mqwR{Ft>fCyBP0ob!VqK zVZsIEyA?vm_s<>K2k0K^#$HDDPEX-)0`^3S8Ky*G+DkNNFhtkV(po~5z*rPbin6(x zpY^Duy)Iv*0dFdFI2g?TObzN;7B8hnd6gFV98i@chm(OW2SZBM!;p_cmu1YC{QBm8 z?b@eLrKvi~cMIJ7&o=Ao&3MpJjxw`AXH6LuSYl_qKmPWO*VSU7YuoCbpGS!w=tgs^ z1nv^(;4h0CT6;0c;Y*PKd;%`*P4d@Dl)q(5Ce5?ZV1QCRpADvfBiDM?WjvpC~3%Y2?QTg?c7)9!`q%wmacXOG`c6S|6!Tj-gliYMLcv#K&c-tr5kOv z%8GEl_@Ml@xKYF z;tmX;Z(lO8YqF^WJtvw{SBm4Qr)Tw?RoEBH_vv|j+xBa5;5VsS8)w^3X$}qQ)(qeS zYCOEwoOd8UgT%T^?-G{@iF>YPez&efFG1#xV}0uO_TXg>T=pC1q8@|>0UE>eCyyOFRJ?K4p>wwi*az6R zB_t$}t0UO&y~}@E+UWq$fxTthoGE}q*3xDkXd^3-v!K~1gFY3yJWc?Akaj0=#4wOt z@!4}wI_xzmaugCj_CFc1z3b5)e|o;nE#dA3XNq6t*{-ob9m)@2T_R2XLi_fWeJ-4p zWlzUIYppXjlIY}Vn#}4|7jIe2h?C4jwnc0ok~oowZHp)}+FyQP+ePtv9*fXTcKgr_ zY>&&Ix%2$nJUG(q-h}oO*FF9%{^&n0$4M?H2|5dp>fGonJ6Q&kqVGLBz^4_GkYIvR zaN`RSFX#7zA}MeZ@)1dCtDdS!=}nBRhsx@tK;ZE7Zz1=zJJK&hLwH5h64g!}=i1xD zrSB<2_rmPCR7Te5nc&lvlaGhFnLn|g1|654;--_RN%Q_x^AKGHTKlkI9OIxX@YOkn zzNOo3{THtNT&mNZ^rTNrkYqV+-(6Ma0Hq=o9#!)8EjwC`xI$7r8fBW(#%1fI$wD2s zKC{|XacZ+lX4X)Fw%|hFj$;xM49`rt*QzW{=A`^J@WRuNJ?52?Eiof2Movo_nDb zCXh-H{TMBM=M}K@u(_OZhY2K>SUxf{Ge7p88JEnmar|8~9?9+&y`7wvLu=Obh`2E| zKID^U0x4t>l`1J$}t{?!-#qfXM@IPF%Avsgjlg}*TOz3mbH#ahze zv(HFoIIZFrylU$F{r$=_ceH*d+b1^Ij@$&_%-eh4vUKQ{!o27rz8xAG>Kro9&DeAP zj1a(xBt)Pmccsmu&~pXeP|J-hm$dHH3Yz;i6Bfn(6Dtjl+>2Ji!zp11pj&60IltYm zt9wO>;6E8z&S(4%#aump2Cc-7%k|fjsu|See4q2B632Fo`iO%W=FQdyO-VYd?r!&5 zi3uZd@&yWe88$o76?ZC}ScyUk*ie;!EPUftYMO2HV(gLNDi|&a@N1t_A8V<5y>#63 zrJyLqS#g%d$#0{FIb0Oi z1SK5*8U6JP&TD$Gmz)QhmQD&?Y*ZM3d5RFW~RMgVHjpTIx(X>=I_Ens- z>*IqD4DO?=7mqCk5#%*s~(to zvBhBO(QVtRs;Uy$gAy$f^~8Du3FA})4_Nh%!2ux=A+d>-Bn>HVx47EU7sTDv6aPXH z!~h%Eq@sWp+*S!N4KF*pv^!<-ep9Wa2}2*%?jIbwv27ZTCr>8rPK|%u))0!ymz8%j z6WrpUx!~o67HAo~qcH-4_4XxF(xSt26w_jK5FcyV8dcKn>V9J&Yj)3j0@d{6yB~Y{ z$}tTUPLpWRsP^xX*4cp1hK!X-CAM{TqnVlVF4t+P{P&!>K1GNJyu#_>wxnXkDy)ZU zGvOwCfcp}EaDMy!gD`(2uFEX4Fa6Suv$`?#ti%*DV+FDMi!{t*8wfDlk$Dk?qK3Xa zvh#q-d0O1&-E!d=C-b|Cu*8+mZnZ`?J!f&COD@j6=gMl&m>(Vf7xPiMe==m<9|xa}aaML4WWZX~JAd9(-qgN=goZ;?5i73FAJk7A;*xrPSdX*`mu6=qmG%o&lqeKSvPTF}IUIvO_uk{(yC z>lk;v&y2>}(bq_>t<^5=|7r8-z&%w)3rC-%iD5IwnEk;aLkz*^G6WCp%{HbK)Qx6h zT%m_Ym9#u*G!yc~cpu!1$QD||=`K2^vKtgt9BqgEP0QpT30&@$CE^}K1TJK^@BN^@ zxBQ;F?D}VBc=f7BGy}snAz|O3BjY;ElyoP3MPky!azM=RXckR-)$BB^8N^B3XhN7@ zng_>ey)1|qX#6xe6zA%46QGqe7KA73krtFo49IC|MZh8OjMg&oP2HY;EhI}tBH+v@ds1M z%)h&Pua7gYMCRWH;*tQm)P9rhC&gLfy=hMl20o!WLyNqpeeX;$X1LyIw`b(L7&4$AuT1V9B{D zy{riD{HT?nRi>9#n)7^Rf`m-Hg8)I4S5A!=~Ev|fSfC?;(U8gRV&66(1OJ-~K=Sa3`kd^BF zva>yL6Z6&kbb7fGqQJ1R-B;YYYD)ooL(2ZH)Uy)X+}>K>+Gj(btSeByo>S7-@~UZ^ zXI)xo5FA-rZ+DQRq|pmAGPD`ydXnMyzSwZn56c4$*KD*$l=8(i_P6Bz`fWd0&7>TZ zvpm_5LxBM>Z16K%{(=^l6NKmj^4)E5?%FMzI}5+W7PwuidA@pczdJ?4G|HSd(A@8L#Hl3zlJ!`gWrSSI z=Jsv$)@QbC(_&Yf&9 z4O2NN0=)Vwz)~m*Q0PbJ?!XOn5jBcb)odGv>B4q-s1#mkKa*E+{C*?zt@Cg*J&;)R z5@y`jLzg7X?!T~!Z6?_2ntQJ(M17uCDa5MeJg;&Kc_6l&A^u!{?rqoc=F2GyLp61E zKuh1b12Khgwg2d6r4&i*&Aaj-<}-$#K2uAF7R{}L+sF?wrbP|LSt28&dwK;z)lThA zYkjeInG!yeAMc#B$RSCN7sojr+AX9xWg;Jl#;Qcd#E9EW;SOj{1ZmjXw*)fmyr8g{ zwyfvwd+x(r;kIJw(VZ@3D|!Xi>&Vz{`teUj`uI<MJ^bhQH!zq z)0JYeThRQsW?7Dtd&`pPu*7V-67Vo@Jp8Oq6lgA}oMR3AGIVNX==~JcYpw&DHXlWl zNiQYjHt&T{-lY+$EP%ODojcscwrR6oSC&kuc!w}hdP^A)MgVF*qSn5ZF$e({^WW!! zbw+l#C{AULzc{61EOfFDdOQ~b&k(JQWAj}~Uc9}MjoW<%G$$4>XXB!2e56eNT0)MNI)wMpui%$-dSW2u^ZH*-zqF4iA9D4flD!9|`~w?Y^<9z}dvoQ#5n zzD)3;WOj0!Vvcki?dd<#Q$!SsLO%!N*!~ZNqH7=5rRaAzEG6qk@0qE9qSo3~5ewV5 zGc$jx_$m4x&`SCGxFS!1*Wm8(w3*_m@G@E)p@TYw*5|G$v^m!J>z>TM_oiaW+YXho zqywVno~{=^yq>u?o8#ghI92{+^q%tMy-Xs@{vwpOzaYJbbWOTHrj^xo@xNWm&OX8OhG){B^Q2orgaP z=vtKJ=`M;4JnR-8)Yi#M#vu(2JkL}_-+Jhp4WYR4ZL`pB5dD zDBl_D{mQ{2DS4~8mv&js0a5;~LFy0n4$B`*cvy-g0Mr(Ta7PkFc^L!f_PQ(K#_g%~ z6O*Q=NUvg}&z}IP#zRr!JggG=t}D(59}pg?+5;YnqM7m6L5ti&npoGie2Yv(On2T) zQO&LiS+cQ$HmR|``d1W*J6YGD0)is`U%Xa~Ydzx;)3*UcV^^Q7@W}4gs>P>`uC~57 zrzYL?L7WTy%2zTe>)f4$vW01L3QdEE2ht{n88}@aOd@=`pU8DQ1w8xuRh(v|mA#cK zV9qKz?w#yrv*vgUsk~qP?9Hl?k0+*bj^EI9Rr;$Bs>aDc=UJ$>%}Am*{M(+S|Bm0# z9Ki@lD6@5E_~jzaaO9!A0$W1;m!CW)p?@b+BBoUS<#zMmT4hP@N+4ur@JVpq`WIs? zk;E(gKhrsd>#6ju^}mI_|5C6M`Th$FmT@H1YEVK_c<}uSZsCRLQ)FY6rYfUFTI~sa zB|OQ_Harnex4;jLuWKT?^bKj0qjQPFcAVkD{Y5|8CO7l6pRdW8c!cag z2F2U4Rv{@@&@>sh6pePXh*cq>02|u+ZQZUN5E1%p*F43ug^7$lo?VS=PK0J2DEGdF zHC!CGe|=J5(Ul2E%RkUs6M7rE^nuvi+B z9i#f?%`cWs*15TlS$-8u&FCqzY4m?HP0vg5*_6^Kp}fLaQM!A2HP2<8ea1JcJ6qzV z&1cUA+mGx{@qYOT8)nZ#@L7Je=hHhmIVo@5Dzf8?x6v~Q<&c7666262ryY`OBKYe0 zB=1z@4BbG;c$SoY?deay>QmfE_=p}7PPVWSQF80IXttP-g!jXQ}^xWkDeQ|8}0;wLKAoK5$p6ww~b%JSj_gezXs+axf$%Hd(D75?P>8Xms^P$N(P-#FYL; zvY?7i&rf}k$4p-3@@|#?8%n_CqIxwH6;iVJ4iro5DKhCdzi`MpSF$DNmn`3epkwjs z67g=Mht*Y@pX+&g*Q$mlu*AF(i-cmHbARjNOn6!4n`u`K^Y-b=X&P*WQV3B;=w9$e zql)r7|MQz+%1J9LdZ10O@NeP5JgSMQZh#iH(zX|V_A8fCD(`{*M0{t|sHgUwDC3(R zc{j(W7Ts8{WWQ}*y=!36?}DghWUGFEx5Ght_XUv*hvbk~@)G%H>KwKQrn8uPxd+(a z->s~5N$qph!wsB~Uw5O_i7{?fmqTyWu)*Q(Ukuj6-S7G0@}u>S{oY@f!TR*T+x^$8 zm0AOEV#R$SqN>(x? z5HsrCxMg%#Xzd_bMOl%{fnsn~u*7zLj>LlrzbGUA&cUL<=ps4w$-5w0N3m$S9uU>p zOyuVYju0@+pziMDPbUV{)|7YudwK|{Ir)k8Oj-`^Xy-^iX6tA}1>L^jxZ#cu=h0_W zQbw^;z&)R15{+@4uKeQ>cQyZvA=SIRaCi7WQoUT9oDD7iFYQpEMN68Re)-e@lxuV~ z^Y2G9@B)r7`JE+aL_rYvrmfBka-y|zH|+KuC{F2JU$lNBA0O(Q^vClLKYT2FdvDzS z?D(}ptF@QA?Vg9HVqgc9lkmcB*6{-sx7Usd#k@*A#F#f3eo1G<@$s1R^HF1UO#^6_ zX`tca0x-1Zu3g6(VXx`#WLn(&@5*y@+M^I(D>d!)sm^}A%}2S+FOOz^v7AJ!KiAJ< zbjL$W<(6g@#s4(~e{uO^h|S)`?ZFel$dRw5Us(tr93PML9ojs8^{!^uEe|wms#UEO z6|omNWyoH9Dg{9#C)16bjXSOn?OD#p$3inkyKR0QQ7k0nk#_$8D2BQ zc(F6)q|Yk$z_8^=WoVn%jXhm%k$*_lZPD|_KR_`8!7UQQ)6r4q^octV)Bxyq4d6DE z-&S`?$HB7z68C;JRE=?ZAE)L<81I~hbro8nDe;i&3E&mV-@Ev?k=kRY6_*Ms+Xu#L zlP%jf^U{&*?B&(Ck7^gB7Z2F4*XmoL%l??fVQF-h(wWIMnfeZ3RPQ|i1YGDP`};qf zS)H2WRy`3-sMv*>4K!+Q97IZH!})|`7%7Oodv|xsa-EwSN}+flf2@pQvwhPW z>p}o_O+>F^>TCtj%QQHSgrWO%!gFH4X58Dllt28xl9_$MZf^V6zDeCWY7#(*w z<1m9V6V|}z3@0%I)QUN!YJKY9}4;M8uCnmzzjoloexb@E>7piJ5?+_yOJTE4&yE~AZ)bqPjg%czo5 zr2WjbiHE`Z#6sb{>oLJny=VT6?V7mr0kc{GjjjfH?wLy6_|Z2ryp|eoJ4A>H^?!?G z8rb!{jXj)Ww}R*hFfcGQ4DezeOWMw!lS82hg+>JyGe5W%AZT+58ppuGHA&I{`uh^= zyJG6JU-#@u^wLNIKfl4b%5*W6vwJiC82>54X>ubb#yHmexqjA~L+0jJKq@B9Nhrza zW@J6Z)QaM-BamQ-{KWgp#y#AsDZ%Z`bJSB>9@wjg`pFAlFqtIUh8P?9y}T(k{yEQu z9#1d;+!{0$mJ*Fo6?kKVmYJ~n;u%^mZS$bb=KH`<3^-ebW6THddg)F2`e=RRt>JjSzYK*_ zB{zh74i|ZE0pZ4IShXfTpQp!b zWqYQ_dOZ4K-Jx>N@ywz1gY|ZqMTC8$=~nkY)AUpgA>nMMJr8xCo&K~kM%EIz1SP;L z!kE88!g43@+_0Mu!)|&=rzr88N(Dq7= z%#oKNAfZrH#g*0cl=As;Q7R6)9V`2HpkPBl4Ty<}X;pz_n|p(oBhB~Ej5zC(0FNW_dPnD` z$H+tO>gyBVW$Sj8#*D^CwR?o@?M7{Nbt!@jx`>QWpL6}uZeo5X{t2xfBA3i71%^Z3 zoq~d`FxC{M`8|+zVNG}UJsGEwua$@XMb%LND{BRwZFF(pikBD8&<*YgKK{B#XkPee z;tlU>Vz|ipq_@qskfETCKC5(_$se%2;$?M~hHY*mG|9Tl(%obKGUjRMP1olt2NsH< zGS;Zld;{b}u*ohjWyFi8sR9caYbLSAcj2`^@Ah(1JwoR{rwpr7+Q*{WJ=XRzAkg|y z7Qzip)krmfXuDswuXlsTn5vS`%gXSzBQ8CaUhSJl|9)$!QBb*W^R-5o%i14Hez#AL zt@*E?`d{DL(3l?h9Ds>mF}!K3yKmnkw@>fi>#=CT8jkkmR*eA+plZ!8q)(os?PdfR zezeeG_>-wMv1#K*(hF9i!6flA`mw3Thr~B-+&BsixNgEHq^Q!;Qn%Ga-I#ot94>5e zO90Jp;rEw?QLNJhAYdV5L)bHa=0|)rk`C?>AmcakWe7M9Q4+&CrP;RNMDK#!Em;u2 zhX;g(g&7?C`;(md(nzQ|80ZF@GF|~ShMb52@q&O(2qNmXl9S^wA&QW%Ds_x_UUf|V z=m+!wWB!rK9F{5 z;b-06;0A&7Oq-eC-~x)M_6}xnWas+9J#E_B!L?k=LpSZDH;&QDK)a%Ry6#&}GF2|McxxfL_nO=rQBT0>2t4 z0a_vNR`#0QK%M~XDIkq}9Fdf?=#ox@Xc!7Xqi$^U(aFgQqIu&z?fi5CwjAYCy|nS+=)6(G03*qQ9dBgY)7>o$ z9)G_2JeDytpl2%vd_jd+84-R3g8e!WcMvO%z``#BLvdfO&i?%?Ak`uDEVA}%8L~ag zcpqKetJPkLZUY8SpA2^t7(2VWD|y|=O4tCwPn`==ycr|K3r8S)CO5PFMzwVr47)_W zPs}n8KQql3trCg&$3f}D8=j~yzkw`03=tVExB2;bd+rg!Ma>0!J$<6N;|2iyS^z=G z|9<+R*w>e+)rx)c0UgR;JwE0AL-6{_0Q%b3z|c^vG(gKOtMZHD@vK?VA|ZsBucM*OX&vbMtfc3ui|dzv2WX08nPM zx>mH|iy4{p)AHvD`gi25f?B7np)GU_;C!)99eFMqWMu>@G6$+pBR`dDc=zVbW)oj5 z^%GWDXlcLt^5)KJ`1lkh6$ylYfEfY<&>T-ksy{Dg1N)Sh|zbo||NM0#wqCGD{ne z2AMs{PQ3wy%5&*i0-z-M7VG2kX~!=`;Xrx|BZEc3S`LgNU7-3!aJ!Ge7Mkoh58XOKM^s!`*Q)XpDRD5kH>}|^yaDzspxi?6Q^5lz&dShsAZ%BJRpg}icEV-F z8FgCdX)wu&;M&i=cZoK&Hwg|Cq>IORI0U3itjg3iG{)X*EDJpi+<}aF9_8!nOIA9( zzCqQuPkNG+oUD=ju`51!jXEMNCl61^+?+S@e?vCGM{v}*Qg2+e+vNNyR)D;g#5ojv z!EzD-;{~Cz<=Ls_Xcde>k?vJ<^D1zogY*lah(=@{0S2!m`o!oVh>D65GrYRTi$f3_{Ag#%MJ6^jBGYKkZEtSQzf#6L#AklC3v7(3$v`C`G!gU8_OTO? zG^$`>&|0z1l+W}sp$~u=0Q*r;ifL@~0<7zw{N4uwiZ*AGqrI>h}&JRl%| zII03Te6;%dI%ro`r06G-rwy9<y(%XZB9kq?AdAS4_7A(D$ZSx;pI3HGSq#&x)fIK zaR6wJqm#omR6vE)vq9>TIHTVBhqpl)!6ofa!Q66yaP`mx<|t zu$(4pS5SOH{QVa>v^=H*-mnZs&HI<<&yqw&YnW&N%wcyRaM4t$!7w$?bEdQO9$Va$ zWf=j!^$Prdp#B}WUXrJd#u1L|@adnYbn%qLa1a6kPcjUY3%2kHg8E$`g0_gN>Y6CA z{blH|bbbA5)9)+bL|VcmTHm`u}k(~|&ZQi%Ai1Nl?|gD9d4nRt$5_W&v{ zi*tAy0PWFE979Ck75l)C*jpmY(Gp?jTE0BK(N6wo2}O1xba~d`Fs!S%850u>;TUV^ zJpzS|W|BJOLm{o`kLbL{Yw8DqZjU0MH`gguUVre`^?hMzM1<0L)phH3AsOSMn9wSc zm-NCDUQDT@54t$RNa|km;6xdYt;JLsWI4mhnqVPkYy<>RB&g^J+m!td2Kwsi{?%{J zpE{LF@YHmEq@|OXjUtknVmNT%0II>plp%1r6=pkBILZd7jRg34EPC+?5Jm=gM^PNPPG1}i@E=t=Kd7HbS--Ke zvU+|0Hxw0l!)?(`P)Ge0i2Z+T>PH8Ey&gR_qPnN&i|Xw0`^JyWHwW;5V zJP)r?a*=*@9jeg`R2dPKF_?t>026fyVRUr|v{Nqu27ZVV!~$C#E4zNZ{4D|2<%W5|x&3I%Dw-Jm`N_-A@8GJseY*w!dQ>GciP<;=mr@L1 z$pLj+mooHYsuzwu23rU?E~opXKNfIeHvR+MFFi`|k=gUi7nY7uu2`;JSb1W#HN}=m}b;KqtZm7Dl1N zt%kHGQ0;V|H#7Sr*}Q049Q1^FgmrYS zt@UN~_!2XxLK}l*b5w8k+baK{HySL+MD!@p`#KbFo^;Ug#Ttfa`Kfyj_V!_pOh~Hza+idJ@oG20TA&S7z%!kYK!Jq5Y=a zYn)I`8tv+lS+TC(Sf1igbxjD)5<7tFL!bVrfzih)YeJ>mi6Getf|kLz@82cVFZ@s&0}$dBQ9P?CR&6y35U&m!@lN-iy3 zF8MO1ATKXOFJd>jB*ibyqt^+D75wa~GLD^>bipNd1O7Q&(;?0vaL z4(P}fMSKlYoBvtIPT`Z53&kRor$yx<%)Rcqh<;i|l{r&35ng?EPWyncutQW3rqU(WN*?U3Nk1EY>2)^%x{ns;FAmi(TrY5!g)D8ke-D#_@Y{^T%i)e+z> zs=54KncBW)P?(1Xl{|8YM9$rfh2-vYFq8_X>&covtNM-Z)2~|m+TJ8CIy$;MFiKE^9qEY-{C}#u5^pT~X#4n@N6IXX)Q2Rg z%tIL?neqw=88SpdhLABzl1j#kq|7pfWGX_2k|~iXGK35jLb&_acmIdGZfmuc^%k%9 zd4A71XYYN^KK+E=>gX__%)OFOdKrVuB#r7tMMY#bN^;`WGDR-Fq;tp=6DZu|1R_oy z8_*uKxQsZ@GJoYtKE9POr7(-eP+??6MtAeEg_u$7XF(R~ZBW{-fD*m|mbCW40WkI= zr6B5rfWpFgsi~%WSCH~}cz9lc{|NqAkoi$+3`tsge)J$o&m@(DRjkNH5;6f?qH3J! zQ^q4LEw7^2zz=MRTt+K#xl@$;j132Lj~dVB_N%{7DRhl|Y7#%(G%`gdwaKgjz}pts zU2m$zRmZ#>MQjHrxEY1!jLz`Zhf@XKvg$6qfk&dB_k{%nU}aMAyJUCNwZ-d8C@ z%rxX(=)8fO5EYcK|A}}bf%zj9EGAuYHT9gvvq&$XJwD*M#Za7lWI}>mF{iZ_kDRF6 zFCM~U;N>m+(?qX#$=`x*Uufg6oLVg*W8h zy_=ZSGY?#dSc*ppb1nl)6ZPoh&ll&nY@vmyC6=3pNCl9y0c=9{8rW(JgjPS&;DLpH zU6;l2Zd0HRq{EG9(t!6e@niN}T5z$C2*P}}?vvPrufPLk_0|FwV8x*S;t8kG#s4H7rZp20dAB$4Qz)jZ)+Chc7@h5%Hu8|i_7Bo*R8zHN}t&6Z*lCxh!K`i=c78nS?+um=-Y8`j=k}ZiRbSwX= zJ4Rkx@7%dV>TCZesd$%M{qz@2s$lSl+#n+(L*^0z;mUIArGvUX=QrSiHb{&MAoET@ zj%5&s0iaVe1LSZHRreGoJ}E+q1F<5A%ARl?7*bcjVql6OA7WH4Xx+?Xe?#F)r4nQC zTik~a$V_B7v)gN_xLSl+L_~x#|J)ZD8Wv`*-Kw9Z*#a=QM>WzL*{H{8X^xdR)rXcp zt<6_E=n}i{N(gcw0gYeoC~bQUCV}+$8zWMJ>LdOrns9`>;~9MeD0(&6{-b7Q+=x!* z4y(;*GPTGbs5E!Mn12E_YG;$v+fP7?9$szM8>&VgO9`C7-oCy~OiWkt z=VLg}FHVo@70nH&l0!iJ&U-Kz$0le@ zp|q=-8RcltHeUymoi*S(;M~{xHT*q-IsA@iB*PlY%a@Tw!Jw}*QAZN%LA2YG!74a{ zt)(`YOrqC{BM{{WcDb^`8;Z=XRfL5s@d9}uxVN>nDeg@U-^#OhBaL{J>#GE+~A=vpr1-&ifn?5ZLJ5KbFNyLZN>Ch2RGdIPF)QNx|GiL%rBP z6vcF2S^lW@Uq@-o$&wYaHh1YV-%F`sw*kOs4`Mx8p+PV8>-KNHZY6P4;gT*h8a6_9 zaC;m( zZ`h>rR7-VzL~DEs#x2#^L@scGz{&Jl^30LQRGxGk1qn=cQ~Vlg=rw78LcPL!YHUu0K`T#>^0OGbw

FbUO!LpDC?~Hh)A( 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