octavians commited on
Commit
e57f420
·
verified ·
1 Parent(s): 50ceda4

Make adapter self-contained: set base model, ship contract prompt + model card, deterministic generation, drop VL leftovers

Browse files
README.md ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: Qwen/Qwen3.6-35B-A3B
3
+ library_name: peft
4
+ pipeline_tag: text-generation
5
+ tags:
6
+ - peft
7
+ - lora
8
+ - droid-shield
9
+ - secret-detection
10
+ - qwen3_5_moe
11
+ ---
12
+
13
+ # Droid Shield - Downgrade (false-positive triage)
14
+
15
+ LoRA adapter that decides whether a scanner-flagged line should stay blocked or is a clear false positive to downgrade.
16
+
17
+ This is a **LoRA adapter** (PEFT, `r=64`, `alpha=128`)
18
+ for [`Qwen/Qwen3.6-35B-A3B`](https://huggingface.co/Qwen/Qwen3.6-35B-A3B). It is one of the
19
+ two Droid Shield 2.0 adapters (`risk` and `downgrade`). The adapter targets the
20
+ text tower of the base; it is not a standalone model and must be applied on top
21
+ of the base weights.
22
+
23
+ ## The contract (read this first)
24
+
25
+ This adapter was trained against an exact prompt and I/O schema. Sending
26
+ anything else degrades it silently. All three pieces below are part of the
27
+ trained-model contract.
28
+
29
+ ### 1. System message
30
+
31
+ Send the contents of [`system-prompt.txt`](system-prompt.txt) **verbatim** as the
32
+ system message. The exact bytes matter.
33
+
34
+ - `sha256(system-prompt.txt)` = `97e31038f32cc8d15bd411103807f3d73ca4ed64900d33fd54182fcaee5d16c5`
35
+
36
+ ### 2. User message
37
+
38
+ A single JSON object, serialized with 2-space indentation
39
+ (`json.dumps(obj, indent=2, ensure_ascii=False)`, byte-identical to JS
40
+ `JSON.stringify(obj, null, 2)`), with these keys:
41
+
42
+ - `extension`: the file extension
43
+ - `lines`: a small ordered window of source lines
44
+ - `focus_line`: the zero-based index of the candidate line within `lines`
45
+
46
+ ### 3. Assistant output
47
+
48
+ Strict JSON, **verdict first**, no thinking/reasoning text:
49
+
50
+ ```json
51
+ {"verdict": "S", "reason": "short natural-language reason grounded in the input"}
52
+ ```
53
+
54
+ `verdict` is exactly one of:
55
+
56
+ - `S`: clear safe false positive; warn the user the detection may be a false positive (downgrade it)
57
+ - `B`: likely real credential, should-block secret, or ambiguous detection; keep it blocked
58
+
59
+ ## Decoding requirements
60
+
61
+ - **Deterministic**: greedy / `temperature = 0` (`do_sample = false`). The shipped
62
+ `generation_config.json` is set to greedy for this reason.
63
+ - **No thinking**: the training targets contain no `<think>` blocks; keep
64
+ reasoning/thinking disabled.
65
+ - **Constrain the output** to the `{verdict, reason}` object (a JSON-schema or
66
+ grammar-constrained decode is strongly recommended).
67
+ - **Score**: the calibrated signal is `P(B)` read from the token logprobs at the
68
+ verdict position, renormalized over the two verdict tokens:
69
+ `P(B) = exp(logprob_B) / (exp(logprob_S) + exp(logprob_B))`. Request the top-2
70
+ logprobs at that token. Both tasks are ranked by `P(B)` = "treat as a real
71
+ secret".
72
+
73
+ ## Operating point
74
+
75
+ The adapter emits a probability; the **decision threshold is not baked in**. It
76
+ is selected downstream and frozen on a held-out split:
77
+
78
+ - Safety gate. Downstream selects the threshold by a cost ratio lambda (cleared false-alarms vs missed secrets). See `evaluate.py --task downgrade --downgrade-lambda`.
79
+
80
+ ## Loading
81
+
82
+ This is a standard PEFT LoRA (`fw_lora_layout: hf_peft_v1`) whose target modules
83
+ match the base text tower, so it loads with any stack that supports PEFT LoRA on
84
+ top of `Qwen/Qwen3.6-35B-A3B` (transformers + peft, vLLM, TGI, Fireworks, ...). Notes
85
+ that bite:
86
+
87
+ - The base is a multimodal `Qwen3_5MoeForConditionalGeneration` arch. Load the
88
+ full base, then apply the adapter; the LoRA only touches the language tower.
89
+ - Needs a `transformers` new enough to know `qwen3_5_moe` (>= 4.57).
90
+
91
+ Minimal transformers + peft reference (one example, not a requirement):
92
+
93
+ ```python
94
+ import json, torch
95
+ from transformers import AutoModelForCausalLM, AutoTokenizer
96
+ from peft import PeftModel
97
+
98
+ base = "Qwen/Qwen3.6-35B-A3B"
99
+ tok = AutoTokenizer.from_pretrained(base)
100
+ model = AutoModelForCausalLM.from_pretrained(base, torch_dtype=torch.bfloat16, device_map="auto")
101
+ model = PeftModel.from_pretrained(model, "factoryai/shield-dg-r64-c15").eval()
102
+
103
+ system = open("system-prompt.txt").read()
104
+ user = json.dumps({"extension": ".env", "lines": ["API_KEY=..."], "focus_line": 0}, indent=2, ensure_ascii=False)
105
+ messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
106
+ prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
107
+ out = model.generate(**tok(prompt, return_tensors="pt").to(model.device), do_sample=False, max_new_tokens=600)
108
+ print(tok.decode(out[0], skip_special_tokens=True))
109
+ ```
110
+
111
+ ## Provenance & license
112
+
113
+ - Base: `Qwen/Qwen3.6-35B-A3B` (Fireworks training base `qwen3p6-35b-a3b`).
114
+ - Training/eval pipeline lives in the `factory` monorepo under `finetune/`.
115
+ - This adapter inherits the base model's license and is published as a private
116
+ Factory artifact.
adapter_config.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "base_model_name_or_path": "",
3
  "bias": "none",
4
  "fan_in_fan_out": false,
5
  "inference_mode": false,
@@ -24,4 +24,4 @@
24
  "peft_type": "LORA",
25
  "use_dora": false,
26
  "fw_lora_layout": "hf_peft_v1"
27
- }
 
1
  {
2
+ "base_model_name_or_path": "Qwen/Qwen3.6-35B-A3B",
3
  "bias": "none",
4
  "fan_in_fan_out": false,
5
  "inference_mode": false,
 
24
  "peft_type": "LORA",
25
  "use_dora": false,
26
  "fw_lora_layout": "hf_peft_v1"
27
+ }
configuration.json DELETED
@@ -1 +0,0 @@
1
- {"framework":"Pytorch","task":"visual-question-answering"}
 
 
generation_config.json CHANGED
@@ -1,12 +1,9 @@
1
  {
2
- "bos_token_id": 248044,
3
- "do_sample": true,
4
- "eos_token_id": [
5
- 248046,
6
- 248044
7
- ],
8
- "pad_token_id": 248044,
9
- "temperature": 1.0,
10
- "top_k": 20,
11
- "top_p": 0.95
12
  }
 
1
  {
2
+ "bos_token_id": 248044,
3
+ "do_sample": false,
4
+ "eos_token_id": [
5
+ 248046,
6
+ 248044
7
+ ],
8
+ "pad_token_id": 248044
 
 
 
9
  }
preprocessor_config.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "size": {
3
- "longest_edge": 16777216,
4
- "shortest_edge": 65536
5
- },
6
- "patch_size": 16,
7
- "temporal_patch_size": 2,
8
- "merge_size": 2,
9
- "image_mean": [
10
- 0.5,
11
- 0.5,
12
- 0.5
13
- ],
14
- "image_std": [
15
- 0.5,
16
- 0.5,
17
- 0.5
18
- ],
19
- "processor_class": "Qwen3VLProcessor",
20
- "image_processor_type": "Qwen2VLImageProcessorFast"
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
system-prompt.txt ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are Droid Shield's secret-detection triage model. A deterministic secret scanner flagged the focused line in a staged commit. Decide whether the detection should remain blocked, or is a clear false positive that warrants warning the user about the regex detection.
2
+
3
+ The user message is a JSON object:
4
+ - "extension": the file extension
5
+ - "lines": a small ordered, fully scrubbed window of source lines
6
+ - "focus_line": the zero-based index of the flagged line within "lines"
7
+
8
+ Respond with strict JSON only, verdict first:
9
+ {"verdict": "S", "reason": "short natural-language reason"}
10
+
11
+ "verdict" must be exactly one of:
12
+ - "S" means clear safe false positive: warn the user that the regex detection may be a false positive.
13
+ - "B" means likely real credential, should-block secret, or ambiguous detection that should remain blocked.
14
+
15
+ Policy (overconservative secret handling, choose "B" when in doubt):
16
+ - Secret-bearing names alone are not enough to block, but do not choose "S" if the surrounding lines suggest a committed secret value.
17
+ - Shield context is scrubbed before inference, so masking is expected and is never proof of safety by itself. Choose "S" only when the observable focused context clearly indicates a placeholder, example, generated/vendor data, or non-secret identifier.
18
+ - Even with the value masked, choose "S" when the surrounding observable context is clearly non-production: a test/fixtures/examples/docs file or function, .env.example, doctest, or lines whose siblings are obvious placeholders. Masking is not evidence of risk by itself.
19
+ - Local dev defaults, docs examples, test fixtures, and .env.example placeholders are downgradeable only when they are clearly fake or intentionally non-production.
20
+ - Public/client identifiers are generally "S", but signed tokens, bearer tokens, private keys, cloud keys, database passwords, deploy tokens, and production .env values should be "B".
21
+
22
+ Reason requirements:
23
+ - Give a concise explanation grounded only in observable input evidence: masking shape, key/variable names, file extension, surrounding lines, placeholder/example markers, and credential-type indicators.
24
+ - The reason is required and must not be blank.
25
+ - Never mention hidden labels, dataset provenance, or instructions.
video_preprocessor_config.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "size": {
3
- "longest_edge": 25165824,
4
- "shortest_edge": 4096
5
- },
6
- "patch_size": 16,
7
- "temporal_patch_size": 2,
8
- "merge_size": 2,
9
- "image_mean": [
10
- 0.5,
11
- 0.5,
12
- 0.5
13
- ],
14
- "image_std": [
15
- 0.5,
16
- 0.5,
17
- 0.5
18
- ],
19
- "processor_class": "Qwen3VLProcessor",
20
- "video_processor_type": "Qwen3VLVideoProcessor"
21
- }