Spaces:
Sleeping
Sleeping
refactor: rename LegalIntern to LMAF (Legal Multi-Agent Framework) (#1)
Browse filesRename package from legal_intern to lmaf, class LegalIntern to LMAF,
update all UI strings, CLI entrypoint, and CI/CD references.
Co-authored-by: overthelex <mcvovkes@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- .github/workflows/deploy.yml +6 -6
- README.md +6 -6
- app.py +8 -8
- pyproject.toml +2 -2
- src/legal_intern/__init__.py +0 -3
- src/lmaf/__init__.py +3 -0
- src/{legal_intern → lmaf}/agents/__init__.py +0 -0
- src/{legal_intern → lmaf}/agents/adjudicator.py +0 -0
- src/{legal_intern → lmaf}/agents/analyst.py +0 -0
- src/{legal_intern → lmaf}/agents/base.py +1 -1
- src/{legal_intern → lmaf}/agents/critic.py +0 -0
- src/{legal_intern → lmaf}/agents/formatter.py +0 -0
- src/{legal_intern → lmaf}/agents/orchestrator.py +0 -0
- src/{legal_intern → lmaf}/agents/planner.py +0 -0
- src/{legal_intern → lmaf}/agents/researcher.py +0 -0
- src/{legal_intern → lmaf}/agents/reviewer.py +0 -0
- src/{legal_intern → lmaf}/agents/surveyor.py +0 -0
- src/{legal_intern → lmaf}/control/__init__.py +0 -0
- src/{legal_intern → lmaf}/core/__init__.py +0 -0
- src/{legal_intern → lmaf}/core/config.py +1 -1
- src/{legal_intern → lmaf}/core/workspace.py +0 -0
- src/{legal_intern → lmaf}/engine.py +4 -20
- src/{legal_intern → lmaf}/main.py +5 -7
- src/{legal_intern → lmaf}/providers/__init__.py +1 -1
- src/{legal_intern → lmaf}/rendering/__init__.py +0 -0
- src/{legal_intern → lmaf}/state/__init__.py +0 -0
- src/{legal_intern → lmaf}/state/loop_state.py +0 -0
- src/{legal_intern → lmaf}/state/research_state.py +0 -0
- src/{legal_intern → lmaf}/tools/__init__.py +0 -0
- src/{legal_intern → lmaf}/tools/secondlayer_bridge.py +0 -0
- src/{legal_intern → lmaf}/verification/__init__.py +0 -0
.github/workflows/deploy.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
name: Deploy
|
| 2 |
|
| 3 |
on:
|
| 4 |
push:
|
|
@@ -6,7 +6,7 @@ on:
|
|
| 6 |
workflow_dispatch:
|
| 7 |
|
| 8 |
concurrency:
|
| 9 |
-
group: deploy-
|
| 10 |
cancel-in-progress: false
|
| 11 |
|
| 12 |
jobs:
|
|
@@ -22,25 +22,25 @@ jobs:
|
|
| 22 |
run: |
|
| 23 |
ssh prod "cd ~/SecondLayer/deployment && \
|
| 24 |
docker compose -f docker-compose.prod.yml --env-file .env.prod \
|
| 25 |
-
build --no-cache
|
| 26 |
|
| 27 |
- name: Start container
|
| 28 |
run: |
|
| 29 |
ssh prod "cd ~/SecondLayer/deployment && \
|
| 30 |
docker compose -f docker-compose.prod.yml --env-file .env.prod \
|
| 31 |
-
up -d
|
| 32 |
|
| 33 |
- name: Health check
|
| 34 |
run: |
|
| 35 |
for i in $(seq 1 45); do
|
| 36 |
-
if ssh prod "docker exec
|
| 37 |
echo "Container healthy after $((i*2))s"
|
| 38 |
exit 0
|
| 39 |
fi
|
| 40 |
sleep 2
|
| 41 |
done
|
| 42 |
echo "Health check failed"
|
| 43 |
-
ssh prod "docker logs --tail 30
|
| 44 |
exit 1
|
| 45 |
|
| 46 |
sync-hf:
|
|
|
|
| 1 |
+
name: Deploy LMAF
|
| 2 |
|
| 3 |
on:
|
| 4 |
push:
|
|
|
|
| 6 |
workflow_dispatch:
|
| 7 |
|
| 8 |
concurrency:
|
| 9 |
+
group: deploy-lmaf
|
| 10 |
cancel-in-progress: false
|
| 11 |
|
| 12 |
jobs:
|
|
|
|
| 22 |
run: |
|
| 23 |
ssh prod "cd ~/SecondLayer/deployment && \
|
| 24 |
docker compose -f docker-compose.prod.yml --env-file .env.prod \
|
| 25 |
+
build --no-cache lmaf-prod"
|
| 26 |
|
| 27 |
- name: Start container
|
| 28 |
run: |
|
| 29 |
ssh prod "cd ~/SecondLayer/deployment && \
|
| 30 |
docker compose -f docker-compose.prod.yml --env-file .env.prod \
|
| 31 |
+
up -d lmaf-prod"
|
| 32 |
|
| 33 |
- name: Health check
|
| 34 |
run: |
|
| 35 |
for i in $(seq 1 45); do
|
| 36 |
+
if ssh prod "docker exec lmaf-prod python -c \"import urllib.request; urllib.request.urlopen('http://localhost:7860/')\"" 2>/dev/null; then
|
| 37 |
echo "Container healthy after $((i*2))s"
|
| 38 |
exit 0
|
| 39 |
fi
|
| 40 |
sleep 2
|
| 41 |
done
|
| 42 |
echo "Health check failed"
|
| 43 |
+
ssh prod "docker logs --tail 30 lmaf-prod" || true
|
| 44 |
exit 1
|
| 45 |
|
| 46 |
sync-hf:
|
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
emoji: ⚖️
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
|
@@ -16,11 +16,11 @@ tags:
|
|
| 16 |
- legal-consultation
|
| 17 |
---
|
| 18 |
|
| 19 |
-
#
|
| 20 |
|
| 21 |
-
A multi-agent
|
| 22 |
|
| 23 |
-
|
| 24 |
|
| 25 |
## Architecture
|
| 26 |
|
|
@@ -103,10 +103,10 @@ export ANTHROPIC_API_KEY=sk-...
|
|
| 103 |
export SECONDLAYER_API_KEY=...
|
| 104 |
|
| 105 |
# Run a consultation
|
| 106 |
-
|
| 107 |
|
| 108 |
# Run from a problem file
|
| 109 |
-
|
| 110 |
```
|
| 111 |
|
| 112 |
## Example Problems
|
|
|
|
| 1 |
---
|
| 2 |
+
title: LMAF
|
| 3 |
emoji: ⚖️
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
|
|
|
| 16 |
- legal-consultation
|
| 17 |
---
|
| 18 |
|
| 19 |
+
# LMAF -- Legal Multi-Agent Framework
|
| 20 |
|
| 21 |
+
A multi-agent framework for **complex legal consultations** over Ukrainian court decisions and legislation.
|
| 22 |
|
| 23 |
+
Built for the legal domain with access to 100M+ Ukrainian court decisions via [SecondLayer](https://legal.org.ua).
|
| 24 |
|
| 25 |
## Architecture
|
| 26 |
|
|
|
|
| 103 |
export SECONDLAYER_API_KEY=...
|
| 104 |
|
| 105 |
# Run a consultation
|
| 106 |
+
lmaf "Чи може продавець стягнути пеню за прострочення оплати товару?"
|
| 107 |
|
| 108 |
# Run from a problem file
|
| 109 |
+
lmaf problems/consumer_penalty.yaml
|
| 110 |
```
|
| 111 |
|
| 112 |
## Example Problems
|
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""
|
| 2 |
|
| 3 |
Chat interface for multi-agent legal consultation pipeline.
|
| 4 |
On prod (agents.legal.org.ua): runs the real pipeline with LLM + SecondLayer API.
|
|
@@ -35,11 +35,11 @@ def has_api_keys() -> bool:
|
|
| 35 |
|
| 36 |
async def _run_pipeline(question: str):
|
| 37 |
"""Run the multi-agent pipeline and yield status updates."""
|
| 38 |
-
from
|
| 39 |
-
from
|
| 40 |
|
| 41 |
config = Config.from_env()
|
| 42 |
-
intern =
|
| 43 |
|
| 44 |
yield "Surveyor: аналізую правовий ландшафт..."
|
| 45 |
result = await intern.surveyor.run(intern.state)
|
|
@@ -85,7 +85,7 @@ async def _run_pipeline(question: str):
|
|
| 85 |
await intern.planner.run(
|
| 86 |
intern.state, revision_critique=critique.details
|
| 87 |
)
|
| 88 |
-
from
|
| 89 |
critique.status = CritiqueStatus.RESOLVED
|
| 90 |
|
| 91 |
if "можна завершувати: True" in critic_result.summary:
|
|
@@ -164,7 +164,7 @@ def stream_chat(message: str, history: list[dict]):
|
|
| 164 |
|
| 165 |
|
| 166 |
ARCHITECTURE_MD = """
|
| 167 |
-
## Архітектура
|
| 168 |
|
| 169 |
Дев'ять спеціалізованих LLM-агентів працюють у циклі. Кожен агент починає з чистого контексту.
|
| 170 |
Весь стан зберігається у структурованому об'єкті `ConsultationState`.
|
|
@@ -237,9 +237,9 @@ DATASETS_MD = """
|
|
| 237 |
|
| 238 |
|
| 239 |
def build_app() -> gr.Blocks:
|
| 240 |
-
with gr.Blocks(title="
|
| 241 |
gr.Markdown(
|
| 242 |
-
"#
|
| 243 |
"### Мульти-агентна система для складних правових консультацій\n\n"
|
| 244 |
"Дев'ять спеціалізованих LLM-агентів аналізують правові питання, "
|
| 245 |
"шукають судову практику у 100М+ рішень ЄДРСР та формують структуровану консультацію.\n\n"
|
|
|
|
| 1 |
+
"""LMAF (Legal Multi-Agent Framework) Gradio chat app.
|
| 2 |
|
| 3 |
Chat interface for multi-agent legal consultation pipeline.
|
| 4 |
On prod (agents.legal.org.ua): runs the real pipeline with LLM + SecondLayer API.
|
|
|
|
| 35 |
|
| 36 |
async def _run_pipeline(question: str):
|
| 37 |
"""Run the multi-agent pipeline and yield status updates."""
|
| 38 |
+
from lmaf.core.config import Config
|
| 39 |
+
from lmaf.engine import LMAF
|
| 40 |
|
| 41 |
config = Config.from_env()
|
| 42 |
+
intern = LMAF(question, config)
|
| 43 |
|
| 44 |
yield "Surveyor: аналізую правовий ландшафт..."
|
| 45 |
result = await intern.surveyor.run(intern.state)
|
|
|
|
| 85 |
await intern.planner.run(
|
| 86 |
intern.state, revision_critique=critique.details
|
| 87 |
)
|
| 88 |
+
from lmaf.state.research_state import CritiqueStatus
|
| 89 |
critique.status = CritiqueStatus.RESOLVED
|
| 90 |
|
| 91 |
if "можна завершувати: True" in critic_result.summary:
|
|
|
|
| 164 |
|
| 165 |
|
| 166 |
ARCHITECTURE_MD = """
|
| 167 |
+
## Архітектура LMAF
|
| 168 |
|
| 169 |
Дев'ять спеціалізованих LLM-агентів працюють у циклі. Кожен агент починає з чистого контексту.
|
| 170 |
Весь стан зберігається у структурованому об'єкті `ConsultationState`.
|
|
|
|
| 237 |
|
| 238 |
|
| 239 |
def build_app() -> gr.Blocks:
|
| 240 |
+
with gr.Blocks(title="LMAF") as app:
|
| 241 |
gr.Markdown(
|
| 242 |
+
"# LMAF -- Legal Multi-Agent Framework\n"
|
| 243 |
"### Мульти-агентна система для складних правових консультацій\n\n"
|
| 244 |
"Дев'ять спеціалізованих LLM-агентів аналізують правові питання, "
|
| 245 |
"шукають судову практику у 100М+ рішень ЄДРСР та формують структуровану консультацію.\n\n"
|
pyproject.toml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
[project]
|
| 2 |
-
name = "
|
| 3 |
version = "0.1.0"
|
| 4 |
description = "Multi-agent scaffolding for complex legal consultations over Ukrainian court decisions"
|
| 5 |
readme = "README.md"
|
|
@@ -35,7 +35,7 @@ all-providers = [
|
|
| 35 |
]
|
| 36 |
|
| 37 |
[project.scripts]
|
| 38 |
-
|
| 39 |
|
| 40 |
[build-system]
|
| 41 |
requires = ["hatchling"]
|
|
|
|
| 1 |
[project]
|
| 2 |
+
name = "lmaf"
|
| 3 |
version = "0.1.0"
|
| 4 |
description = "Multi-agent scaffolding for complex legal consultations over Ukrainian court decisions"
|
| 5 |
readme = "README.md"
|
|
|
|
| 35 |
]
|
| 36 |
|
| 37 |
[project.scripts]
|
| 38 |
+
lmaf = "lmaf.main:cli"
|
| 39 |
|
| 40 |
[build-system]
|
| 41 |
requires = ["hatchling"]
|
src/legal_intern/__init__.py
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
"""LegalIntern -- multi-agent scaffolding for complex legal consultations."""
|
| 2 |
-
|
| 3 |
-
__version__ = "0.1.0"
|
|
|
|
|
|
|
|
|
|
|
|
src/lmaf/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""LMAF -- Legal Multi-Agent Framework for complex legal consultations."""
|
| 2 |
+
|
| 3 |
+
__version__ = "0.1.0"
|
src/{legal_intern → lmaf}/agents/__init__.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/adjudicator.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/analyst.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/base.py
RENAMED
|
@@ -28,7 +28,7 @@ class AgentResult:
|
|
| 28 |
|
| 29 |
|
| 30 |
class BaseAgent(ABC):
|
| 31 |
-
"""Base class for all
|
| 32 |
|
| 33 |
role: str = "base"
|
| 34 |
description: str = ""
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
class BaseAgent(ABC):
|
| 31 |
+
"""Base class for all LMAF agents."""
|
| 32 |
|
| 33 |
role: str = "base"
|
| 34 |
description: str = ""
|
src/{legal_intern → lmaf}/agents/critic.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/formatter.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/orchestrator.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/planner.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/researcher.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/reviewer.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/agents/surveyor.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/control/__init__.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/core/__init__.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/core/config.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""Configuration for the
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 1 |
+
"""Configuration for the LMAF system."""
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
src/{legal_intern → lmaf}/core/workspace.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/engine.py
RENAMED
|
@@ -1,20 +1,4 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
| 3 |
-
Nine-agent pipeline for complex legal consultations:
|
| 4 |
-
|
| 5 |
-
1. Surveyor -- maps the legal landscape (runs once)
|
| 6 |
-
2. Planner -- produces/revises research strategy
|
| 7 |
-
3. Orchestrator -- dispatches tasks to researcher or analyst
|
| 8 |
-
4. Researcher -- finds case law, legislation, doctrine (via SecondLayer MCP)
|
| 9 |
-
5. Analyst -- computes deadlines, penalties, procedural checks
|
| 10 |
-
6. Reviewer -- adversarial verification of evidence (auto-triggered)
|
| 11 |
-
7. Critic -- periodic strategy audit (every N iterations)
|
| 12 |
-
8. Adjudicator -- resolves inter-agent disagreements
|
| 13 |
-
9. Formatter -- produces final consultation document
|
| 14 |
-
|
| 15 |
-
No agent carries conversation history. All state lives in ConsultationState.
|
| 16 |
-
The workspace is git-versioned for full reproducibility.
|
| 17 |
-
"""
|
| 18 |
|
| 19 |
from __future__ import annotations
|
| 20 |
|
|
@@ -43,8 +27,8 @@ from .state.research_state import ConsultationState, CritiqueStatus
|
|
| 43 |
console = Console()
|
| 44 |
|
| 45 |
|
| 46 |
-
class
|
| 47 |
-
"""Main loop for the
|
| 48 |
|
| 49 |
def __init__(self, question: str, config: Config | None = None) -> None:
|
| 50 |
self.config = config or Config.from_env()
|
|
@@ -70,7 +54,7 @@ class LegalIntern:
|
|
| 70 |
|
| 71 |
async def run(self) -> str:
|
| 72 |
"""Execute the full consultation pipeline. Returns the final answer."""
|
| 73 |
-
console.print(Panel(f"[bold]
|
| 74 |
|
| 75 |
# Phase 1: Survey
|
| 76 |
console.print("[dim]Phase 1: Surveyor[/dim]")
|
|
|
|
| 1 |
+
"""LMAF -- Legal Multi-Agent Framework main loop engine."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 27 |
console = Console()
|
| 28 |
|
| 29 |
|
| 30 |
+
class LMAF:
|
| 31 |
+
"""Main loop for the Legal Multi-Agent Framework."""
|
| 32 |
|
| 33 |
def __init__(self, question: str, config: Config | None = None) -> None:
|
| 34 |
self.config = config or Config.from_env()
|
|
|
|
| 54 |
|
| 55 |
async def run(self) -> str:
|
| 56 |
"""Execute the full consultation pipeline. Returns the final answer."""
|
| 57 |
+
console.print(Panel(f"[bold]LMAF[/bold]\n{self.state.client_question[:200]}"))
|
| 58 |
|
| 59 |
# Phase 1: Survey
|
| 60 |
console.print("[dim]Phase 1: Surveyor[/dim]")
|
src/{legal_intern → lmaf}/main.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""CLI entrypoint for
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
@@ -8,11 +8,11 @@ import sys
|
|
| 8 |
from pathlib import Path
|
| 9 |
|
| 10 |
from .core.config import Config
|
| 11 |
-
from .engine import
|
| 12 |
|
| 13 |
|
| 14 |
def cli() -> None:
|
| 15 |
-
parser = argparse.ArgumentParser(description="
|
| 16 |
parser.add_argument("question", nargs="?", help="Legal question (or path to .yaml problem file)")
|
| 17 |
parser.add_argument("--model", default=None, help="Override default LLM model")
|
| 18 |
parser.add_argument("--max-iterations", type=int, default=None)
|
|
@@ -24,7 +24,6 @@ def cli() -> None:
|
|
| 24 |
parser.print_help()
|
| 25 |
sys.exit(1)
|
| 26 |
|
| 27 |
-
# Load config
|
| 28 |
if args.config and args.config.exists():
|
| 29 |
config = Config.from_yaml(args.config)
|
| 30 |
else:
|
|
@@ -37,7 +36,6 @@ def cli() -> None:
|
|
| 37 |
if args.workspace_dir:
|
| 38 |
config.workspace_dir = args.workspace_dir
|
| 39 |
|
| 40 |
-
# Load question from file or use directly
|
| 41 |
question = args.question
|
| 42 |
if Path(question).exists():
|
| 43 |
import yaml
|
|
@@ -45,8 +43,8 @@ def cli() -> None:
|
|
| 45 |
data = yaml.safe_load(Path(question).read_text())
|
| 46 |
question = data.get("question", data.get("problem", question))
|
| 47 |
|
| 48 |
-
|
| 49 |
-
answer = asyncio.run(
|
| 50 |
|
| 51 |
print("\n" + "=" * 60)
|
| 52 |
print(answer)
|
|
|
|
| 1 |
+
"""CLI entrypoint for LMAF."""
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 8 |
from pathlib import Path
|
| 9 |
|
| 10 |
from .core.config import Config
|
| 11 |
+
from .engine import LMAF
|
| 12 |
|
| 13 |
|
| 14 |
def cli() -> None:
|
| 15 |
+
parser = argparse.ArgumentParser(description="LMAF -- Legal Multi-Agent Framework")
|
| 16 |
parser.add_argument("question", nargs="?", help="Legal question (or path to .yaml problem file)")
|
| 17 |
parser.add_argument("--model", default=None, help="Override default LLM model")
|
| 18 |
parser.add_argument("--max-iterations", type=int, default=None)
|
|
|
|
| 24 |
parser.print_help()
|
| 25 |
sys.exit(1)
|
| 26 |
|
|
|
|
| 27 |
if args.config and args.config.exists():
|
| 28 |
config = Config.from_yaml(args.config)
|
| 29 |
else:
|
|
|
|
| 36 |
if args.workspace_dir:
|
| 37 |
config.workspace_dir = args.workspace_dir
|
| 38 |
|
|
|
|
| 39 |
question = args.question
|
| 40 |
if Path(question).exists():
|
| 41 |
import yaml
|
|
|
|
| 43 |
data = yaml.safe_load(Path(question).read_text())
|
| 44 |
question = data.get("question", data.get("problem", question))
|
| 45 |
|
| 46 |
+
engine = LMAF(question, config)
|
| 47 |
+
answer = asyncio.run(engine.run())
|
| 48 |
|
| 49 |
print("\n" + "=" * 60)
|
| 50 |
print(answer)
|
src/{legal_intern → lmaf}/providers/__init__.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""LLM provider
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 1 |
+
"""LMAF LLM provider -- AWS Bedrock only."""
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
src/{legal_intern → lmaf}/rendering/__init__.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/state/__init__.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/state/loop_state.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/state/research_state.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/tools/__init__.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/tools/secondlayer_bridge.py
RENAMED
|
File without changes
|
src/{legal_intern → lmaf}/verification/__init__.py
RENAMED
|
File without changes
|