usejul/jul-decision-wemm-4b

Model

jul-decision-wemm-4b

0

4 commits

1 linked in READMEs

updated Oct 6, 2026

See the code

README

jul-decision-wemm-4b

LoRA adapters for tencent/WeMM-Embedding-4B that answer jul's typed questions — pick an option (Choice), yes or no (Noul), a score (Score) — reading the question and the text together. jul's default model on PyTorch.

from jul import TypeSafeClient, Choice, Noul

client = TypeSafeClient()          # jul-decision-wemm-4b: tencent/WeMM-Embedding-4B + these adapters
response = client.system_one(
    state={"ticket": "I was charged twice for my subscription this month."},
    questions={
        "team": Choice(instructions="Which team should handle this ticket?",
                       criteria={"billing": "payments, invoices, refunds",
                                 "technical": "bugs, errors, crashes",
                                 "sales": "pricing, plans, demos"}),
        "is_bug": Noul(instructions="Does the message report a software bug?"),
    },
)

Needs jul after 0.3.0 (today main, then the next release), with the PyTorch backend (jul[torch]). On a Mac with MLX, jul's default model reads with vectors only until these adapters are measured in 4-bit.

Files

  • adapter.npz: rank-16 LoRA adapters (A and B, float16) on the 248 Linear layers of the decoder, 32.5M parameters, 65 MB. Switched off, the model is WeMM-Embedding-4B unchanged; jul switches them on only for the reading below, so the vector reading of the same model is untouched.
  • cross_heads.npz: one head per question type — noul (3 classes), score (1), choice_q and choice_k (a bilinear head, 2560 → 256).
  • cross.json: the prompts and the heads, read by jul.

Reading

  • Noul, Score: Text: "<text>", Question: <question> (and Candidate answer: <level> for a Score), Verdict:, read on the last token of the last layer after the final norm.
  • Choice: the text, the question and every option in one prompt, one pass for any number of options. Each option is read on the newline that closes it (it has seen the text, the question and the options before it), the answer on Answer:, scored by the bilinear head. jul mixes it with the vector reading of the same model: log p = log p_vector + 3 · log p_listwise.
Text: "<text>"
Question: <question>
Options:
- <option 1>
- <option 2>
Answer:

Results

Decision bench: 2,108 typed questions over 12 task families, English and French, with texts written for the bench and never seen in training. PyTorch bf16 on one A10G GPU, one question per call; Jev (TypeSafe's hosted decision model) through its API, network included in its latency.

FamilynJevjul-decision-wemm-4b
emotion600.9330.817
finance1800.7560.850
hard negatives2260.8230.836
intent2340.8420.868
legal1580.9110.873
moderation1790.8770.804
inference (NLI)2080.8170.731
relational1010.9410.921
routing1180.8980.831
sentiment1200.9830.925
support2940.8980.864
topic2300.9170.891
Choice9170.9240.883
Noul (yes/no)8950.8780.872
Score2960.6990.672
English1,2540.8710.862
French8540.8760.829
All2,1080.8730.849
p50 latency655 ms97 ms

WeMM-Embedding-4B read as vectors, with the earlier adapters for Noul and Score only (usejul/jul-decision-wemm-4b-4bit-mlx), scores 0.757 on the same bench (Choice 0.700).

A family has 60 to 300 questions, so one row moves by ±4 to ±12 points. The bench shares its task families and label sets with the training data, not its texts. On four label sets never trained on (Yahoo topics, EmpatheticDialogues, MASSIVE, Financial PhraseBank; 1,453 texts), Choice goes from 0.707 with the vectors to 0.730. Emotion on real conversations barely moves: it learnt the traps of written sarcasm more than it reads emotion.

Training

WeMM-Embedding-4B frozen (bf16); adapters and heads trained on public English and French datasets under commercial-use licenses, with no text of the bench nor of the dev sets:

  1. Choice questions over nine families (routing, hard negatives, sentiment, emotion, intent, support, topic, finance, legal; 2 to 21 options, reshuffled every time a row is used) together with Noul and Score (relational yes/no, single-text decisions, scores): 2,000 steps on one L40S GPU. Loss: log score + 0.5 × Brier score.
  2. Continued on hard emotion questions (sarcasm, irony, negated praise, mixed feelings; English and French) with twice as many replayed rows of step 1: 567 steps.

License

Apache-2.0, as WeMM-Embedding-4B (Tencent), which these adapters are built on.

lora
typed-decisions
zero-shot-classification

usejul/jul-decision-wemm-4b

Model

jul-decision-wemm-4b

0

4 commits

1 linked in READMEs

updated Oct 6, 2026

See the code

README

jul-decision-wemm-4b

LoRA adapters for tencent/WeMM-Embedding-4B that answer jul's typed questions — pick an option (Choice), yes or no (Noul), a score (Score) — reading the question and the text together. jul's default model on PyTorch.

from jul import TypeSafeClient, Choice, Noul

client = TypeSafeClient()          # jul-decision-wemm-4b: tencent/WeMM-Embedding-4B + these adapters
response = client.system_one(
    state={"ticket": "I was charged twice for my subscription this month."},
    questions={
        "team": Choice(instructions="Which team should handle this ticket?",
                       criteria={"billing": "payments, invoices, refunds",
                                 "technical": "bugs, errors, crashes",
                                 "sales": "pricing, plans, demos"}),
        "is_bug": Noul(instructions="Does the message report a software bug?"),
    },
)

Needs jul after 0.3.0 (today main, then the next release), with the PyTorch backend (jul[torch]). On a Mac with MLX, jul's default model reads with vectors only until these adapters are measured in 4-bit.

Files

  • adapter.npz: rank-16 LoRA adapters (A and B, float16) on the 248 Linear layers of the decoder, 32.5M parameters, 65 MB. Switched off, the model is WeMM-Embedding-4B unchanged; jul switches them on only for the reading below, so the vector reading of the same model is untouched.
  • cross_heads.npz: one head per question type — noul (3 classes), score (1), choice_q and choice_k (a bilinear head, 2560 → 256).
  • cross.json: the prompts and the heads, read by jul.

Reading

  • Noul, Score: Text: "<text>", Question: <question> (and Candidate answer: <level> for a Score), Verdict:, read on the last token of the last layer after the final norm.
  • Choice: the text, the question and every option in one prompt, one pass for any number of options. Each option is read on the newline that closes it (it has seen the text, the question and the options before it), the answer on Answer:, scored by the bilinear head. jul mixes it with the vector reading of the same model: log p = log p_vector + 3 · log p_listwise.
Text: "<text>"
Question: <question>
Options:
- <option 1>
- <option 2>
Answer:

Results

Decision bench: 2,108 typed questions over 12 task families, English and French, with texts written for the bench and never seen in training. PyTorch bf16 on one A10G GPU, one question per call; Jev (TypeSafe's hosted decision model) through its API, network included in its latency.

FamilynJevjul-decision-wemm-4b
emotion600.9330.817
finance1800.7560.850
hard negatives2260.8230.836
intent2340.8420.868
legal1580.9110.873
moderation1790.8770.804
inference (NLI)2080.8170.731
relational1010.9410.921
routing1180.8980.831
sentiment1200.9830.925
support2940.8980.864
topic2300.9170.891
Choice9170.9240.883
Noul (yes/no)8950.8780.872
Score2960.6990.672
English1,2540.8710.862
French8540.8760.829
All2,1080.8730.849
p50 latency655 ms97 ms

WeMM-Embedding-4B read as vectors, with the earlier adapters for Noul and Score only (usejul/jul-decision-wemm-4b-4bit-mlx), scores 0.757 on the same bench (Choice 0.700).

A family has 60 to 300 questions, so one row moves by ±4 to ±12 points. The bench shares its task families and label sets with the training data, not its texts. On four label sets never trained on (Yahoo topics, EmpatheticDialogues, MASSIVE, Financial PhraseBank; 1,453 texts), Choice goes from 0.707 with the vectors to 0.730. Emotion on real conversations barely moves: it learnt the traps of written sarcasm more than it reads emotion.

Training

WeMM-Embedding-4B frozen (bf16); adapters and heads trained on public English and French datasets under commercial-use licenses, with no text of the bench nor of the dev sets:

  1. Choice questions over nine families (routing, hard negatives, sentiment, emotion, intent, support, topic, finance, legal; 2 to 21 options, reshuffled every time a row is used) together with Noul and Score (relational yes/no, single-text decisions, scores): 2,000 steps on one L40S GPU. Loss: log score + 0.5 × Brier score.
  2. Continued on hard emotion questions (sarcasm, irony, negated praise, mixed feelings; English and French) with twice as many replayed rows of step 1: 567 steps.

License

Apache-2.0, as WeMM-Embedding-4B (Tencent), which these adapters are built on.

lora
typed-decisions
zero-shot-classification