Lexai vovkes222 Claude Opus 4.6 commited on
Commit
3bd9d04
·
unverified ·
1 Parent(s): e56db6a

refactor: rename LegalIntern to LMAF (Legal Multi-Agent Framework) (#1)

Browse files

Rename 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>

Files changed (31) hide show
  1. .github/workflows/deploy.yml +6 -6
  2. README.md +6 -6
  3. app.py +8 -8
  4. pyproject.toml +2 -2
  5. src/legal_intern/__init__.py +0 -3
  6. src/lmaf/__init__.py +3 -0
  7. src/{legal_intern → lmaf}/agents/__init__.py +0 -0
  8. src/{legal_intern → lmaf}/agents/adjudicator.py +0 -0
  9. src/{legal_intern → lmaf}/agents/analyst.py +0 -0
  10. src/{legal_intern → lmaf}/agents/base.py +1 -1
  11. src/{legal_intern → lmaf}/agents/critic.py +0 -0
  12. src/{legal_intern → lmaf}/agents/formatter.py +0 -0
  13. src/{legal_intern → lmaf}/agents/orchestrator.py +0 -0
  14. src/{legal_intern → lmaf}/agents/planner.py +0 -0
  15. src/{legal_intern → lmaf}/agents/researcher.py +0 -0
  16. src/{legal_intern → lmaf}/agents/reviewer.py +0 -0
  17. src/{legal_intern → lmaf}/agents/surveyor.py +0 -0
  18. src/{legal_intern → lmaf}/control/__init__.py +0 -0
  19. src/{legal_intern → lmaf}/core/__init__.py +0 -0
  20. src/{legal_intern → lmaf}/core/config.py +1 -1
  21. src/{legal_intern → lmaf}/core/workspace.py +0 -0
  22. src/{legal_intern → lmaf}/engine.py +4 -20
  23. src/{legal_intern → lmaf}/main.py +5 -7
  24. src/{legal_intern → lmaf}/providers/__init__.py +1 -1
  25. src/{legal_intern → lmaf}/rendering/__init__.py +0 -0
  26. src/{legal_intern → lmaf}/state/__init__.py +0 -0
  27. src/{legal_intern → lmaf}/state/loop_state.py +0 -0
  28. src/{legal_intern → lmaf}/state/research_state.py +0 -0
  29. src/{legal_intern → lmaf}/tools/__init__.py +0 -0
  30. src/{legal_intern → lmaf}/tools/secondlayer_bridge.py +0 -0
  31. src/{legal_intern → lmaf}/verification/__init__.py +0 -0
.github/workflows/deploy.yml CHANGED
@@ -1,4 +1,4 @@
1
- name: Deploy LegalIntern
2
 
3
  on:
4
  push:
@@ -6,7 +6,7 @@ on:
6
  workflow_dispatch:
7
 
8
  concurrency:
9
- group: deploy-legal-intern
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 legal-intern-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 legal-intern-prod"
32
 
33
  - name: Health check
34
  run: |
35
  for i in $(seq 1 45); do
36
- if ssh prod "docker exec legal-intern-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 legal-intern-prod" || true
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: LegalIntern
3
  emoji: ⚖️
4
  colorFrom: blue
5
  colorTo: indigo
@@ -16,11 +16,11 @@ tags:
16
  - legal-consultation
17
  ---
18
 
19
- # LegalIntern
20
 
21
- A multi-agent scaffolding system for **complex legal consultations** over Ukrainian court decisions and legislation.
22
 
23
- Inspired by [PhysicsIntern](https://huggingface.co/spaces/huggingface/physics-intern)'s multi-agent research pipeline, adapted for the legal domain with access to 100M+ Ukrainian court decisions via [SecondLayer](https://legal.org.ua).
24
 
25
  ## Architecture
26
 
@@ -103,10 +103,10 @@ export ANTHROPIC_API_KEY=sk-...
103
  export SECONDLAYER_API_KEY=...
104
 
105
  # Run a consultation
106
- legal-intern "Чи може продавець стягнути пеню за прострочення оплати товару?"
107
 
108
  # Run from a problem file
109
- legal-intern problems/consumer_penalty.yaml
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
- """LegalIntern 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,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 legal_intern.core.config import Config
39
- from legal_intern.engine import LegalIntern
40
 
41
  config = Config.from_env()
42
- intern = LegalIntern(question, config)
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 legal_intern.state.research_state import CritiqueStatus
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
- ## Архітектура LegalIntern
168
 
169
  Дев'ять спеціалізованих LLM-агентів працюють у циклі. Кожен агент починає з чистого контексту.
170
  Весь стан зберігається у структурованому об'єкті `ConsultationState`.
@@ -237,9 +237,9 @@ DATASETS_MD = """
237
 
238
 
239
  def build_app() -> gr.Blocks:
240
- with gr.Blocks(title="LegalIntern") as app:
241
  gr.Markdown(
242
- "# LegalIntern\n"
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 = "legal-intern"
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
- legal-intern = "legal_intern.main:cli"
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 LegalIntern agents."""
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 LegalIntern system."""
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
- """LegalIntern main loop engine.
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 LegalIntern:
47
- """Main loop for the LegalIntern consultation system."""
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]LegalIntern[/bold]\n{self.state.client_question[:200]}"))
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 LegalIntern."""
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 LegalIntern
12
 
13
 
14
  def cli() -> None:
15
- parser = argparse.ArgumentParser(description="LegalIntern -- multi-agent legal consultation")
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
- intern = LegalIntern(question, config)
49
- answer = asyncio.run(intern.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 abstraction layer -- AWS Bedrock only."""
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