setup models

This commit is contained in:
Marcos Lohmann
2026-10-07 13:43:09 -03:00
parent aeee6c7de1
commit 84ea9a5307
75 changed files with 5093 additions and 58 deletions

14
adapters/__init__.py Normal file
View File

@@ -0,0 +1,14 @@
"""Adaptadores JEV locais. Cada candidato roda num ambiente próprio (dependências incompatíveis)."""
from importlib import import_module
REGISTRY = {
"laya": "adapters.laya.adapter:LayaAdapter",
"semif": "adapters.semif.adapter:SemIfAdapter",
"gliner": "adapters.gliner.adapter:GlinerAdapter",
"rizzo_flow": "adapters.rizzo_flow.adapter:RizzoFlowAdapter",
}
def adapter_class(name: str):
module, cls = REGISTRY[name].split(":")
return getattr(import_module(module), cls)

View File

@@ -0,0 +1,55 @@
"""Adaptador do GLiNER2.5-multi-Decide via classify_text (tarefa 44).
O GLiNER devolve só o rótulo vencedor no modo de rótulo único. Para obter a
distribuição completa, cada pergunta é enviada com `multi_label=True`,
`class_act="softmax"` e `cls_threshold=0`: o modelo calcula o mesmo softmax do modo
de rótulo único e devolve todas as opções com sua probabilidade.
"""
from __future__ import annotations
from dmb.adapter import Adapter
from dmb.jev import ContextLimit, answer_from_distribution, option_descriptions
class GlinerAdapter(Adapter):
name = "gliner"
probabilities_source = "native"
def load(self) -> None:
from gliner2 import AutoExtractor
from tokenizers import Tokenizer
self.model = AutoExtractor.from_pretrained(str(self.model_dir))
if self.device not in ("auto", "cpu"):
self.model.to(self.device)
elif self.device == "auto":
import torch
if torch.cuda.is_available():
self.model.to("cuda")
self.model.eval()
self.tokenizer = Tokenizer.from_file(str(self.model_dir / "tokenizer.json"))
def native_context_limit(self) -> int:
return int(getattr(self.model.config, "max_len", 512) or 512)
def count_tokens(self, text: str) -> int:
return len(self.tokenizer.encode(text, add_special_tokens=False).ids)
def decide(self, state: str, questions: dict) -> tuple[dict, int]:
tokens = self.count_tokens(state)
if tokens > self.context_limit:
raise ContextLimit(f"{tokens} tokens > limite {self.context_limit}")
# As instruções entram como prefixo do texto: o classify_text não tem campo de pergunta.
answers = {}
for name, question in questions.items():
labels = option_descriptions(question)
text = f"Pergunta: {question['instructions']}\n{state}"
raw = self.model.classify_text(
text,
{name: {"labels": labels, "multi_label": True, "class_act": "softmax",
"cls_threshold": 0.0}},
include_confidence=True,
)[name]
dist = {item["label"]: item["confidence"] for item in raw}
answers[name] = answer_from_distribution(question, dist)
return answers, tokens

51
adapters/laya/adapter.py Normal file
View File

@@ -0,0 +1,51 @@
"""Adaptador do Laya multilingual (tarefa 42)."""
from __future__ import annotations
from pathlib import Path
from dmb.adapter import Adapter
from dmb.jev import ContextLimit, answer_from_distribution, option_keys
class LayaAdapter(Adapter):
name = "laya"
# Laya arredonda as probabilidades em 4 casas; o piso comum de dmb.jev cobre os zeros.
probabilities_source = "native"
def load(self) -> None:
import shutil
import tempfile
import laya
from tokenizers import Tokenizer
# laya.load() regrava tokenizer/tokenizer_config.json no próprio diretório
# (_fix_tokenizer_config). Carregamos de uma cópia para não alterar os artefatos verificados.
self._workdir = Path(tempfile.mkdtemp(prefix="dmb-laya-")) / "model"
shutil.copytree(self.model_dir, self._workdir)
self.agent = laya.load(str(self._workdir))
self.tokenizer = Tokenizer.from_file(str(self.model_dir / "tokenizer" / "tokenizer.json"))
def native_context_limit(self) -> int:
return 1024 # padrão do checkpoint; 256 desses tokens vão para pergunta + opções
def count_tokens(self, text: str) -> int:
return len(self.tokenizer.encode(text, add_special_tokens=False).ids)
def decide(self, state: str, questions: dict) -> tuple[dict, int]:
result = self.agent.predict(state, questions, max_len=self.context_limit)
usage = result.get("usage", {})
if usage.get("truncated") or usage.get("state_tokens_dropped"):
raise ContextLimit(f"Laya truncou o estado: {usage}")
answers = {}
for name, question in questions.items():
raw = result["answers"][name]
if question["type"] == "noul":
p = float(raw["noul"])
dist = {"true": p, "false": 1 - p}
else:
dist = raw["probabilities"]
if question["type"] == "score":
dist = {k: dist[k] for k in option_keys(question)}
answers[name] = answer_from_distribution(question, dist, raw.get("confidence"))
return answers, int(usage.get("input_tokens", 0))

View File

@@ -0,0 +1,59 @@
"""Adaptador do Rizzo Flow 4B (GGUF Q8_0 sobre llama.cpp) com prompts spark-decisions-v3 (tarefa 45)."""
from __future__ import annotations
from pathlib import Path
from dmb.adapter import Adapter
from dmb.jev import ContextLimit, answer_from_distribution, option_keys
GGUF_FILE = "spark-x2.5-4b-rizzo-flow-lora-q8_0.gguf"
class RizzoFlowAdapter(Adapter):
name = "rizzo_flow"
probabilities_source = "native"
def __init__(self, *args, runtime_dir: str | Path | None = None, **kwargs):
super().__init__(*args, **kwargs)
self.runtime_dir = runtime_dir
def load(self) -> None:
from rizzo_flow.backend_llama import LlamaBackend
from rizzo_flow.compat import model_name
from rizzo_flow.engine import Engine
device = "cuda" if self.device == "cuda" else ("cpu" if self.device == "cpu" else "auto")
backend = LlamaBackend.load(self.model_dir / GGUF_FILE, device=device, ctx=self.context_limit,
runtime_dir=self.runtime_dir)
self.engine = Engine(backend, ctx=self.context_limit)
self.served = model_name(backend.metadata)
def native_context_limit(self) -> int:
return 2048 # comprimento máximo dos prompts de treino do LoRA
def count_tokens(self, text: str) -> int:
return len(self.engine.backend.tokenizer.encode(text))
def decide(self, state: str, questions: dict) -> tuple[dict, int]:
from rizzo_flow.compat import SystemOneRequest, from_native, to_native
request = SystemOneRequest.model_validate(
{"model": self.served, "state": state, "questions": questions})
native, options = to_native(request)
try:
raw = from_native(request, self.engine.decide(native), options, self.served)
except ValueError as error:
if "context" in str(error).lower() or "tokens" in str(error).lower():
raise ContextLimit(str(error)) from error
raise
raw = raw if isinstance(raw, dict) else raw.model_dump()
answers = {}
for name, question in questions.items():
a = raw["answers"][name]
if question["type"] == "noul":
p = float(a["noul"])
dist = {"true": p, "false": 1 - p}
else:
dist = {k: a["probabilities"][k] for k in option_keys(question)}
answers[name] = answer_from_distribution(question, dist, a.get("confidence"))
return answers, int(raw.get("usage", {}).get("input_tokens", 0))

45
adapters/semif/adapter.py Normal file
View File

@@ -0,0 +1,45 @@
"""Adaptador do SemIf (modo direct) com Qwen3.5-4B fixado (tarefa 43)."""
from __future__ import annotations
from dmb.adapter import Adapter
from dmb.jev import ContextLimit, answer_from_distribution, option_descriptions
class SemIfAdapter(Adapter):
name = "semif"
probabilities_source = "native"
def load(self) -> None:
from semif_phase1 import core, direct
dev = self.device
if dev == "auto":
import torch
dev = "cuda" if torch.cuda.is_available() else "cpu"
self._direct = direct
self.model, self.tokenizer, self.metadata = core.load_causal_model(
str(self.model_dir), self.revision, device=dev, dtype="bfloat16")
def native_context_limit(self) -> int:
return 8192
def count_tokens(self, text: str) -> int:
return len(self.tokenizer.encode(text, add_special_tokens=False))
def decide(self, state: str, questions: dict) -> tuple[dict, int]:
answers, total_tokens = {}, 0
for name, question in questions.items():
options = option_descriptions(question)
row = {"id": name, "state": state, "question": question["instructions"],
"options": [{"id": k, "description": v} for k, v in options.items()]}
try:
out = self._direct.score(self.model, self.tokenizer, row, self.metadata,
max_tokens=self.context_limit)
except ValueError as error:
if "exceed limit" in str(error):
raise ContextLimit(str(error)) from error
raise
dist = dict(zip(out["option_ids"], out["probabilities"]))
answers[name] = answer_from_distribution(question, dist)
total_tokens += out["input_tokens"]
return answers, total_tokens