Text Generation
Transformers
Safetensors
GGUF
MLX
qwen3
lora
regulatory
compliance
ontology-extraction
information-extraction
flowx
conversational
Eval Results (legacy)
text-generation-inference
Instructions to use flowxai/semantic-mapper with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use flowxai/semantic-mapper with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="flowxai/semantic-mapper") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("flowxai/semantic-mapper") model = AutoModelForCausalLM.from_pretrained("flowxai/semantic-mapper", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - MLX
How to use flowxai/semantic-mapper with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("flowxai/semantic-mapper") prompt = "Write a story about Einstein" messages = [{"role": "user", "content": prompt}] prompt = tokenizer.apply_chat_template( messages, add_generation_prompt=True ) text = generate(model, tokenizer, prompt=prompt, verbose=True) - llama-cpp-python
How to use flowxai/semantic-mapper with llama-cpp-python:
# !pip install llama-cpp-python from llama_cpp import Llama llm = Llama.from_pretrained( repo_id="flowxai/semantic-mapper", filename="gguf/semantic-mapper-4b-Q4_K_M.gguf", )
llm.create_chat_completion( messages = [ { "role": "user", "content": "What is the capital of France?" } ] ) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use flowxai/semantic-mapper with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf flowxai/semantic-mapper:Q4_K_M # Run inference directly in the terminal: llama cli -hf flowxai/semantic-mapper:Q4_K_M
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf flowxai/semantic-mapper:Q4_K_M # Run inference directly in the terminal: llama cli -hf flowxai/semantic-mapper:Q4_K_M
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf flowxai/semantic-mapper:Q4_K_M # Run inference directly in the terminal: ./llama-cli -hf flowxai/semantic-mapper:Q4_K_M
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf flowxai/semantic-mapper:Q4_K_M # Run inference directly in the terminal: ./build/bin/llama-cli -hf flowxai/semantic-mapper:Q4_K_M
Use Docker
docker model run hf.co/flowxai/semantic-mapper:Q4_K_M
- LM Studio
- Jan
- vLLM
How to use flowxai/semantic-mapper with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "flowxai/semantic-mapper" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "flowxai/semantic-mapper", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/flowxai/semantic-mapper:Q4_K_M
- SGLang
How to use flowxai/semantic-mapper with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "flowxai/semantic-mapper" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "flowxai/semantic-mapper", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "flowxai/semantic-mapper" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "flowxai/semantic-mapper", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Ollama
How to use flowxai/semantic-mapper with Ollama:
ollama run hf.co/flowxai/semantic-mapper:Q4_K_M
- Unsloth Studio
How to use flowxai/semantic-mapper with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for flowxai/semantic-mapper to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for flowxai/semantic-mapper to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for flowxai/semantic-mapper to start chatting
- Pi
How to use flowxai/semantic-mapper with Pi:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "flowxai/semantic-mapper"
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "mlx-lm": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "flowxai/semantic-mapper" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Hermes Agent new
How to use flowxai/semantic-mapper with Hermes Agent:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "flowxai/semantic-mapper"
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default flowxai/semantic-mapper
Run Hermes
hermes
- Atomic Chat new
- OpenClaw new
How to use flowxai/semantic-mapper with OpenClaw:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "flowxai/semantic-mapper"
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "flowxai/semantic-mapper" \ --custom-provider-id mlx-lm \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
- MLX LM
How to use flowxai/semantic-mapper with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Interactive chat REPL mlx_lm.chat --model "flowxai/semantic-mapper"
Run an OpenAI-compatible server
# Install MLX LM uv tool install mlx-lm # Start the server mlx_lm.server --model "flowxai/semantic-mapper" # Calling the OpenAI-compatible server with curl curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "flowxai/semantic-mapper", "messages": [ {"role": "user", "content": "Hello"} ] }' - Docker Model Runner
How to use flowxai/semantic-mapper with Docker Model Runner:
docker model run hf.co/flowxai/semantic-mapper:Q4_K_M
- Lemonade
How to use flowxai/semantic-mapper with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull flowxai/semantic-mapper:Q4_K_M
Run and chat with the model
lemonade run user.semantic-mapper-Q4_K_M
List all available models
lemonade list
Add README.md
Browse files
README.md
CHANGED
|
@@ -1,9 +1,367 @@
|
|
| 1 |
---
|
| 2 |
-
library_name: mlx
|
| 3 |
license: apache-2.0
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
pipeline_tag: text-generation
|
| 6 |
-
base_model:
|
| 7 |
tags:
|
| 8 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
|
|
|
| 2 |
license: apache-2.0
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
- fr
|
| 6 |
+
- de
|
| 7 |
+
- ro
|
| 8 |
+
library_name: transformers
|
| 9 |
pipeline_tag: text-generation
|
| 10 |
+
base_model: Qwen/Qwen3-4B
|
| 11 |
tags:
|
| 12 |
+
- lora
|
| 13 |
+
- regulatory
|
| 14 |
+
- compliance
|
| 15 |
+
- ontology-extraction
|
| 16 |
+
- information-extraction
|
| 17 |
+
- mlx
|
| 18 |
+
- gguf
|
| 19 |
+
- flowx
|
| 20 |
+
model-index:
|
| 21 |
+
- name: semantic-mapper
|
| 22 |
+
results:
|
| 23 |
+
- task:
|
| 24 |
+
type: text-generation
|
| 25 |
+
name: Regulatory ontology extraction (held-out, document-disjoint, EN/FR/DE/RO)
|
| 26 |
+
metrics:
|
| 27 |
+
- type: f1
|
| 28 |
+
name: concepts F1 (controlled 252-concept vocab)
|
| 29 |
+
value: 0.54
|
| 30 |
+
- type: f1
|
| 31 |
+
name: concepts F1 (fuzzy / semantic match)
|
| 32 |
+
value: 0.58
|
| 33 |
+
- type: f1
|
| 34 |
+
name: domain_tags F1 (free-text)
|
| 35 |
+
value: 0.55
|
| 36 |
+
- type: accuracy
|
| 37 |
+
name: entities field-accuracy
|
| 38 |
+
value: 0.53
|
| 39 |
+
- type: accuracy
|
| 40 |
+
name: JSON validity
|
| 41 |
+
value: 1.0
|
| 42 |
+
- type: accuracy
|
| 43 |
+
name: all-3-facets present
|
| 44 |
+
value: 1.0
|
| 45 |
---
|
| 46 |
+
|
| 47 |
+
## Inference contract
|
| 48 |
+
|
| 49 |
+
Running this model correctly requires its **frozen inference contract**: the exact
|
| 50 |
+
system prompt, user-turn format, decode settings, and output JSON schema it was
|
| 51 |
+
trained against. See [`inference_contract/`](./inference_contract):
|
| 52 |
+
|
| 53 |
+
- [`INFERENCE.md`](./inference_contract/INFERENCE.md): wiring guide: system prompt, user turn (`Extract ontology from this chunk:\n\nCHUNK:\n<text>`), decode settings (`enable_thinking=False`, greedy/temp 0, `max_new_tokens` ~1024), and the note that `semantic.concepts` is constrained to the **252-concept controlled vocabulary** (`concept_taxonomy.yaml`, shipped at repo root).
|
| 54 |
+
- [`prompt_mapper_sys_v1.txt`](./inference_contract/prompt_mapper_sys_v1.txt): the system prompt, verbatim.
|
| 55 |
+
- [`schema_mapper_v1.json`](./inference_contract/schema_mapper_v1.json): output JSON Schema (structural / semantic / governance).
|
| 56 |
+
|
| 57 |
+
Prompt version `mapper_sys_v1`. Do not edit the prompt/schema; the weights are trained against them.
|
| 58 |
+
|
| 59 |
+
# FlowX Semantic Mapper (4B): the regulation-to-ontology tagger
|
| 60 |
+
|
| 61 |
+
**Reads one chunk of regulatory or legal text and emits a structured ontology JSON: where the clause sits in its source, what it is about, and how it maps to policy and escalation.** Languages: EN, FR, DE, RO. Domains: banking, insurance, logistics, labor.
|
| 62 |
+
|
| 63 |
+
Given a single clause (a subsection of a state insurance code, an EU regulation article, a labor-code paragraph, an ADR packing rule), the Mapper returns three facets: a **structural** locator (source id, document type, and the full hierarchy path), a **semantic** payload (free-text `domain_tags`, controlled-vocabulary `concepts`, and an actor/action/object/constraint entity triple), and a **governance** payload (`PDP.<domain>.<rule>` policy references plus a single `if ... THEN escalate` guard clause).
|
| 64 |
+
|
| 65 |
+
The product insight that shapes everything: **an ontology field is only useful if it is consistent enough to join on.** The first version emitted `concepts` as open free text and produced 1062 unique ids, 88% of them singletons: unlearnable for the model and unmeasurable for us (concepts F1 sat at 0.24). Collapsing that to a **252-concept controlled taxonomy** (`concept_taxonomy.yaml`) is the central design choice of this release: it made `concepts` both learnable and scoreable (F1 0.24 → 0.54), and gave downstream policy code a stable key to join on. `domain_tags` stay free-text on purpose (for recall and human browsing) and are honestly noisier as a result.
|
| 66 |
+
|
| 67 |
+
The Mapper is the first stage of a pipeline, not a standalone product: **Mapper tags a chunk → a policy layer → the [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate) escalation model decides DECIDE vs ESCALATE.** It is part of the FlowX on-device model family alongside [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate), [`flowxai/caveat`](https://huggingface.co/flowxai/caveat), and the consumer scam detectors [`flowxai/scam-guard-qwen06b`](https://huggingface.co/flowxai/scam-guard-qwen06b) / [`flowxai/scam-guard-qwen17b`](https://huggingface.co/flowxai/scam-guard-qwen17b).
|
| 68 |
+
|
| 69 |
+
LoRA-fine-tuned from Apache-2.0 **`Qwen/Qwen3-4B`**. Formats: fp16 safetensors (transformers / CUDA / vLLM), **MLX-quantized** int4 + int8 (Apple Silicon), and **GGUF** `Q8_0` + `Q4_K_M` (llama.cpp / CPU / Ollama).
|
| 70 |
+
|
| 71 |
+
---
|
| 72 |
+
|
| 73 |
+
## How do I use it?
|
| 74 |
+
|
| 75 |
+
Two copy-pasteable ways to turn a regulatory chunk into ontology JSON, plus a note.
|
| 76 |
+
|
| 77 |
+
Real example input (a real clause from the California Insurance Code, section 790.03(h)(2)):
|
| 78 |
+
|
| 79 |
+
```
|
| 80 |
+
Failing to acknowledge and act reasonably promptly upon communications with respect
|
| 81 |
+
to claims arising under insurance policies.
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
The user turn wraps it in the fixed template (see the inference contract):
|
| 85 |
+
|
| 86 |
+
```
|
| 87 |
+
Extract ontology from this chunk:
|
| 88 |
+
|
| 89 |
+
CHUNK:
|
| 90 |
+
Failing to acknowledge and act reasonably promptly upon communications with respect to claims arising under insurance policies.
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
### (a) transformers / CUDA (fp16 safetensors)
|
| 94 |
+
|
| 95 |
+
```python
|
| 96 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 97 |
+
|
| 98 |
+
SYSTEM_PROMPT = (
|
| 99 |
+
"You are a legal and regulatory ontology extractor.\n"
|
| 100 |
+
"Extract structured tags from document chunks. Output ONLY valid JSON."
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
tok = AutoTokenizer.from_pretrained("flowxai/semantic-mapper")
|
| 104 |
+
model = AutoModelForCausalLM.from_pretrained("flowxai/semantic-mapper", device_map="cuda")
|
| 105 |
+
|
| 106 |
+
chunk = ("Failing to acknowledge and act reasonably promptly upon communications "
|
| 107 |
+
"with respect to claims arising under insurance policies.")
|
| 108 |
+
user = f"Extract ontology from this chunk:\n\nCHUNK:\n{chunk}"
|
| 109 |
+
|
| 110 |
+
prompt = tok.apply_chat_template(
|
| 111 |
+
[{"role": "system", "content": SYSTEM_PROMPT},
|
| 112 |
+
{"role": "user", "content": user}],
|
| 113 |
+
add_generation_prompt=True, enable_thinking=False, tokenize=False,
|
| 114 |
+
)
|
| 115 |
+
ids = tok(prompt, return_tensors="pt").to(model.device)
|
| 116 |
+
out = model.generate(**ids, max_new_tokens=1024, do_sample=False, temperature=0.0)
|
| 117 |
+
raw = tok.decode(out[0][ids.input_ids.shape[1]:], skip_special_tokens=True)
|
| 118 |
+
```
|
| 119 |
+
|
| 120 |
+
### (b) MLX (Apple Silicon)
|
| 121 |
+
|
| 122 |
+
Off the MLX-quantized weights (int4 / int8):
|
| 123 |
+
|
| 124 |
+
```bash
|
| 125 |
+
mlx_lm.generate --model flowxai/semantic-mapper-mlx-int4 --temp 0 --max-tokens 1024 \
|
| 126 |
+
--system-prompt "$(printf 'You are a legal and regulatory ontology extractor.\nExtract structured tags from document chunks. Output ONLY valid JSON.')" \
|
| 127 |
+
--prompt "$(printf 'Extract ontology from this chunk:\n\nCHUNK:\nFailing to acknowledge and act reasonably promptly upon communications with respect to claims arising under insurance policies.')"
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
```python
|
| 131 |
+
from mlx_lm import load, generate
|
| 132 |
+
|
| 133 |
+
SYSTEM_PROMPT = (
|
| 134 |
+
"You are a legal and regulatory ontology extractor.\n"
|
| 135 |
+
"Extract structured tags from document chunks. Output ONLY valid JSON."
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
model, tok = load("flowxai/semantic-mapper-mlx-int4")
|
| 139 |
+
chunk = ("Failing to acknowledge and act reasonably promptly upon communications "
|
| 140 |
+
"with respect to claims arising under insurance policies.")
|
| 141 |
+
user = f"Extract ontology from this chunk:\n\nCHUNK:\n{chunk}"
|
| 142 |
+
prompt = tok.apply_chat_template(
|
| 143 |
+
[{"role": "system", "content": SYSTEM_PROMPT},
|
| 144 |
+
{"role": "user", "content": user}],
|
| 145 |
+
add_generation_prompt=True, enable_thinking=False,
|
| 146 |
+
)
|
| 147 |
+
raw = generate(model, tok, prompt=prompt, max_tokens=1024, verbose=False)
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
Either backend returns the **same JSON** for this chunk (this is the real held-out target for the example above):
|
| 151 |
+
|
| 152 |
+
```json
|
| 153 |
+
{
|
| 154 |
+
"structural": {
|
| 155 |
+
"source_id": "US_CA_INS_790_03_h_2",
|
| 156 |
+
"hierarchy": [
|
| 157 |
+
"state_code", "california_insurance_code",
|
| 158 |
+
"section", "790.03",
|
| 159 |
+
"subdivision", "h",
|
| 160 |
+
"paragraph", "2"
|
| 161 |
+
],
|
| 162 |
+
"document_type": "state_insurance_code"
|
| 163 |
+
},
|
| 164 |
+
"semantic": {
|
| 165 |
+
"domain_tags": ["insurance_regulation", "claims", "unfair_claims_practices", "settlement"],
|
| 166 |
+
"concepts": ["claim_acknowledgement", "prompt_communication", "settlement_timeline"],
|
| 167 |
+
"entities": {
|
| 168 |
+
"actor": "insurer",
|
| 169 |
+
"action": "acknowledge and act reasonably promptly upon communications",
|
| 170 |
+
"object": "claim_communication",
|
| 171 |
+
"constraint": { "condition": "reasonably_prompt" }
|
| 172 |
+
}
|
| 173 |
+
},
|
| 174 |
+
"governance": {
|
| 175 |
+
"policy_references": ["PDP.insurance.claims_settlement", "PDP.insurance.unfair_practices"],
|
| 176 |
+
"escalation_trigger": "if !claim_communication_acknowledged_promptly THEN escalate"
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
### (c) A short note on decoding
|
| 182 |
+
|
| 183 |
+
`enable_thinking=False` is not optional. Qwen3-4B is a thinking model, but this
|
| 184 |
+
adapter was trained on pure JSON with no reasoning block, so the default template
|
| 185 |
+
yields empty output. Decode greedily (temperature 0), one chunk per call, and cap
|
| 186 |
+
at ~1024 new tokens. GGUF (`Q8_0` / `Q4_K_M`) runs the same contract on
|
| 187 |
+
llama.cpp / Ollama. Full wiring is in [`inference_contract/INFERENCE.md`](./inference_contract/INFERENCE.md).
|
| 188 |
+
|
| 189 |
+
---
|
| 190 |
+
|
| 191 |
+
## How it works
|
| 192 |
+
|
| 193 |
+
```
|
| 194 |
+
regulatory
|
| 195 |
+
text chunk ---> FlowX Semantic Mapper 4B ---> structural (source_id, hierarchy, document_type)
|
| 196 |
+
|
|
| 197 |
+
+--> semantic (domain_tags, concepts, entities)
|
| 198 |
+
|
|
| 199 |
+
+--> governance (policy_references, escalation_trigger)
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
Pipeline line:
|
| 203 |
+
|
| 204 |
+
```
|
| 205 |
+
chunk --> Semantic Mapper (this model) --> policy layer --> Sentinel Gate --> DECIDE | ESCALATE
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
Under the hood the Mapper is: `tokenizer → Qwen3-4B (LoRA fine-tuned) → greedy JSON
|
| 209 |
+
decode (thinking disabled) → parse against schema_mapper_v1.json`. The
|
| 210 |
+
`semantic.concepts` field targets the 252-concept controlled taxonomy
|
| 211 |
+
(`concept_taxonomy.yaml`), so downstream code can filter out-of-vocabulary ids and
|
| 212 |
+
join the rest straight into the policy layer.
|
| 213 |
+
|
| 214 |
+
---
|
| 215 |
+
|
| 216 |
+
## Output schema
|
| 217 |
+
|
| 218 |
+
A single JSON object with three facets (full schema:
|
| 219 |
+
[`schema_mapper_v1.json`](./inference_contract/schema_mapper_v1.json)):
|
| 220 |
+
|
| 221 |
+
**`structural`**: where the clause lives.
|
| 222 |
+
- `source_id`: stable id for the chunk, usually an uppercased normalization of the citation path (e.g. `US_CA_INS_790_03_h_2`).
|
| 223 |
+
- `hierarchy`: ordered flat array of alternating `[level, value]` pairs from the outermost container down to the leaf.
|
| 224 |
+
- `document_type`: snake_case document class (e.g. `state_insurance_code`, `eu_regulation`, `labor_code`, `adr_agreement`).
|
| 225 |
+
|
| 226 |
+
**`semantic`**: what the clause is about.
|
| 227 |
+
- `domain_tags`: free snake_case topic tags (**open vocabulary**; noisier by design, tuned for recall/browsing).
|
| 228 |
+
- `concepts`: concept ids from the **252-concept controlled taxonomy** (`concept_taxonomy.yaml`). Treat any id not in the taxonomy as out-of-vocabulary.
|
| 229 |
+
- `entities`: `{actor, action, object, constraint}`: who the obligation falls on, what must/may be done, on what, and a free key/value `constraint` map of qualifying conditions (e.g. `{"condition":"reasonably_prompt"}`).
|
| 230 |
+
|
| 231 |
+
**`governance`**: how it maps to policy and escalation.
|
| 232 |
+
- `policy_references`: ids in the form `PDP.<domain>.<rule>` (e.g. `PDP.insurance.claims_settlement`).
|
| 233 |
+
- `escalation_trigger`: a single guard clause `if <condition> THEN escalate`, consumed by the Sentinel Gate.
|
| 234 |
+
|
| 235 |
+
The controlled taxonomy holds **252 canonical concepts** across 6 categories
|
| 236 |
+
(money, rights_waived, time_renewal, lease, insurance, data), each with an `id`, a
|
| 237 |
+
`definition`, and a `primary_domain`. It ships as `concept_taxonomy.yaml` at the
|
| 238 |
+
repo root.
|
| 239 |
+
|
| 240 |
+
---
|
| 241 |
+
|
| 242 |
+
## Evaluation
|
| 243 |
+
|
| 244 |
+
Held-out set: **n = 112**, **document-disjoint** from training, multilingual
|
| 245 |
+
(EN / FR / DE / RO). Read the honest numbers first, then the caveat.
|
| 246 |
+
|
| 247 |
+
| Metric | Value |
|
| 248 |
+
| --- | --- |
|
| 249 |
+
| JSON validity | **1.00** |
|
| 250 |
+
| all-3-facets present | **1.00** |
|
| 251 |
+
| `domain_tags` F1 (free-text) | 0.55 |
|
| 252 |
+
| `concepts` F1 (controlled 252-concept vocab) | **0.54** |
|
| 253 |
+
| `concepts` F1 (fuzzy / semantic match) | 0.58 |
|
| 254 |
+
| `entities` field-accuracy | 0.53 |
|
| 255 |
+
|
| 256 |
+
**The headline improvement, quantified.** Adopting the controlled 252-concept
|
| 257 |
+
vocabulary plus a real-regulation data expansion moved `concepts` F1 from **0.24**
|
| 258 |
+
(open free-text vocab) to **0.54**, and `entities` from **unmeasured** (the facet
|
| 259 |
+
was empty in the first corpus) to **0.53**. The format facets are saturated: JSON
|
| 260 |
+
validity and all-three-facets-present are both 1.00, so the model reliably produces
|
| 261 |
+
a parseable, structurally-complete object every time.
|
| 262 |
+
|
| 263 |
+
**Honest reading.**
|
| 264 |
+
- `concepts` is **partial** at ~0.54: it surfaces many of the right controlled ids, not all of them. Use it as a strong signal, not a complete tagging.
|
| 265 |
+
- `domain_tags` are **free-text** and therefore less consistent than the controlled `concepts` (this is the intended trade: recall/browsability over join-stability).
|
| 266 |
+
- `entities` field-accuracy of 0.53 reflects a genuinely hard four-slot extraction (actor/action/object/constraint), now measurable where before it was not.
|
| 267 |
+
- **EN is strongest.** FR, DE, RO are supported and evaluated, but a bit weaker.
|
| 268 |
+
|
| 269 |
+
> **Home-field caveat (read before citing these numbers).** The held-out set is
|
| 270 |
+
> **document-disjoint** (no shared source document leaks between train and test),
|
| 271 |
+
> which is the right discipline. But the annotations across train and test share a
|
| 272 |
+
> **lineage**: the same controlled taxonomy and the same frontier-model-assisted
|
| 273 |
+
> annotation style. The scores above measure how well the model reproduces *that
|
| 274 |
+
> annotation convention* on unseen documents, which is exactly the deployment task,
|
| 275 |
+
> but they are **not** a claim of agreement with an independent human legal
|
| 276 |
+
> annotator. Treat 0.54 concepts F1 as "reproduces the FlowX taxonomy convention on
|
| 277 |
+
> held-out documents at ~0.54", not "a legal-grade ontology at 0.54".
|
| 278 |
+
|
| 279 |
+
---
|
| 280 |
+
|
| 281 |
+
## Formats
|
| 282 |
+
|
| 283 |
+
| Format | Path | Runtime | Notes |
|
| 284 |
+
| --- | --- | --- | --- |
|
| 285 |
+
| fp16 safetensors | repo root | transformers / CUDA / vLLM | full precision |
|
| 286 |
+
| MLX int4 | `mlx-int4/` | Apple Silicon (MLX) | smallest Apple footprint |
|
| 287 |
+
| MLX int8 | `mlx-int8/` | Apple Silicon (MLX) | higher-fidelity Apple path |
|
| 288 |
+
| GGUF `Q8_0` | `gguf/` | llama.cpp / CPU / Ollama | recommended GGUF quant |
|
| 289 |
+
| GGUF `Q4_K_M` | `gguf/` | llama.cpp / CPU / Ollama | smallest GGUF |
|
| 290 |
+
|
| 291 |
+
All formats run the same frozen inference contract (system prompt, user-turn
|
| 292 |
+
template, `enable_thinking=False`, greedy decode).
|
| 293 |
+
|
| 294 |
+
---
|
| 295 |
+
|
| 296 |
+
## Intended use & limitations
|
| 297 |
+
|
| 298 |
+
**Intended use.** A **first-stage extractor** in a regulatory-compliance pipeline:
|
| 299 |
+
it turns a clause of legislation into a structured ontology record that a policy
|
| 300 |
+
layer and the [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate)
|
| 301 |
+
escalation model can act on. It is meant to run over a knowledge base of regulatory
|
| 302 |
+
chunks and produce consistent, joinable tags, with a human reviewing anything the
|
| 303 |
+
Sentinel routes for escalation.
|
| 304 |
+
|
| 305 |
+
**Out of scope & limitations.**
|
| 306 |
+
- **Not legal advice.** The Mapper labels the structure and topic of a text; it does not interpret the law, give a legal opinion, or determine compliance. A human with legal responsibility owns any decision.
|
| 307 |
+
- **Extraction is partial.** `concepts` F1 ~0.54 and `entities` accuracy ~0.53: expect misses and the occasional wrong slot. Do not treat the output as an exhaustive or authoritative tagging.
|
| 308 |
+
- **Controlled vocab, not free-form.** `semantic.concepts` targets the 252-concept taxonomy. Filter out-of-vocabulary ids before joining. `domain_tags` are free-text and inherently noisier.
|
| 309 |
+
- **Language skew.** EN is strongest; FR / DE / RO are supported but weaker.
|
| 310 |
+
- **One chunk at a time.** Trained on single-chunk turns; batching multiple clauses into one user turn degrades output.
|
| 311 |
+
- **Domain coverage.** Banking, insurance, logistics, labor. Text outside these domains is out of distribution.
|
| 312 |
+
- **Annotation-lineage caveat** (see Evaluation): scores measure reproduction of the FlowX annotation convention, not agreement with an independent legal annotator.
|
| 313 |
+
|
| 314 |
+
---
|
| 315 |
+
|
| 316 |
+
## Training data
|
| 317 |
+
|
| 318 |
+
- **763 records total**, split **651 train / 112 held-out**, **document-disjoint** (no source document appears in both splits).
|
| 319 |
+
- **Real, verbatim regulatory / legislative text** from official public sources: US eCFR & state codes, EU EUR-Lex, France Légifrance, Germany Gesetze im Internet, Romania legislatie.just.ro, and UNECE/ADR. Every record carries a source URL. Official legislation is reproduced with source acknowledgement.
|
| 320 |
+
- Annotated to the **controlled 252-concept taxonomy** with frontier-model assistance, then validated: concepts checked in-vocabulary, entities checked populated, source URL required per record.
|
| 321 |
+
- Coverage spans four domains (banking / insurance / logistics / labor) and four languages (EN / FR / DE / RO).
|
| 322 |
+
|
| 323 |
+
The controlled taxonomy (`concept_taxonomy.yaml`) is the pivot of this dataset:
|
| 324 |
+
open free-text concepts (1062 unique, 88% singletons) were collapsed to 252
|
| 325 |
+
canonical ids, which is what made the `concepts` facet learnable and measurable.
|
| 326 |
+
|
| 327 |
+
---
|
| 328 |
+
|
| 329 |
+
## Fine-tuning
|
| 330 |
+
|
| 331 |
+
**LoRA** on **`Qwen/Qwen3-4B`** with **MLX-LM on Apple Silicon**.
|
| 332 |
+
|
| 333 |
+
| Hyperparameter | Value |
|
| 334 |
+
| --- | --- |
|
| 335 |
+
| method | LoRA |
|
| 336 |
+
| rank | 32 |
|
| 337 |
+
| scale (alpha) | 16 |
|
| 338 |
+
| dropout | 0.05 |
|
| 339 |
+
| layers | `num_layers -1` (all) |
|
| 340 |
+
| epochs | ~3 |
|
| 341 |
+
| LR schedule | cosine 1e-4 → 1e-5 |
|
| 342 |
+
| max_seq_length | 2048 |
|
| 343 |
+
| seed | 42 |
|
| 344 |
+
| thinking | disabled |
|
| 345 |
+
|
| 346 |
+
The assistant turn in training is the exact ontology JSON the model must emit (no
|
| 347 |
+
reasoning block), which is why inference must set `enable_thinking=False`.
|
| 348 |
+
|
| 349 |
+
---
|
| 350 |
+
|
| 351 |
+
## Not legal advice
|
| 352 |
+
|
| 353 |
+
This model extracts structure and topic tags from legal/regulatory text. It does
|
| 354 |
+
not provide legal advice, does not interpret the law, and does not determine
|
| 355 |
+
compliance. Its output is an aid to a compliance pipeline under human ownership,
|
| 356 |
+
not a substitute for a qualified professional.
|
| 357 |
+
|
| 358 |
+
## Links
|
| 359 |
+
|
| 360 |
+
- **Sibling (escalation gate):** [`flowxai/sentinel-gate`](https://huggingface.co/flowxai/sentinel-gate): consumes a case (with the Mapper's governance facet) and decides DECIDE vs ESCALATE.
|
| 361 |
+
- **FlowX on-device family:** [`flowxai/caveat`](https://huggingface.co/flowxai/caveat), [`flowxai/scam-guard-qwen06b`](https://huggingface.co/flowxai/scam-guard-qwen06b), [`flowxai/scam-guard-qwen17b`](https://huggingface.co/flowxai/scam-guard-qwen17b).
|
| 362 |
+
|
| 363 |
+
## License
|
| 364 |
+
|
| 365 |
+
Apache-2.0 (weights and code). Base model `Qwen/Qwen3-4B` is Apache-2.0. Copyright
|
| 366 |
+
2026 FlowX.AI; see the `NOTICE` file. Author: Bogdan Răduță, Head of Research,
|
| 367 |
+
FlowX.AI.
|