Ameen commited on
Commit
706f886
·
verified ·
1 Parent(s): 3b7d988

Upload OSINT-Micro - Lightweight ONNX model for microservices (5.7GB)

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ onnx/model_q4f16.onnx_data filter=lfs diff=lfs merge=lfs -text
37
+ onnx/model_q4f16.onnx_data_1 filter=lfs diff=lfs merge=lfs -text
38
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: Qwen/Qwen3-4B
4
+ tags:
5
+ - qwen3
6
+ - investigative-journalism
7
+ - osint
8
+ - onnx
9
+ - transformers.js
10
+ - webgpu
11
+ - conversational
12
+ ---
13
+
14
+ # qwen3-4b-journalist-ONNX
15
+
16
+ An investigative journalism model fine-tuned on [Qwen/Qwen3-4B](https://huggingface.co/Qwen/Qwen3-4B), converted to ONNX for in-browser inference via [Transformers.js](https://github.com/huggingface/transformers.js) and WebGPU.
17
+
18
+ Built by [Buried Signals](https://buriedsignals.com) for local/browser OSINT assistant workflows.
19
+
20
+ ## Usage
21
+
22
+ ```javascript
23
+ import { AutoTokenizer, AutoModelForCausalLM } from "@huggingface/transformers";
24
+
25
+ const tokenizer = await AutoTokenizer.from_pretrained("tomvaillant/qwen3-4b-journalist-ONNX");
26
+ const model = await AutoModelForCausalLM.from_pretrained("tomvaillant/qwen3-4b-journalist-ONNX", {
27
+ dtype: "q4f16",
28
+ device: "webgpu",
29
+ });
30
+ ```
31
+
32
+ ## Training
33
+
34
+ - **Method:** QLoRA with [Unsloth](https://github.com/unslothai/unsloth) / TRL SFT
35
+ - **Data:** 700 instruction/response pairs across investigative journalism and OSINT categories
36
+ - **Task:** browser-sized assistant for OSINT tool selection, investigation planning, verification, and source handling
37
+
38
+ ## ONNX Conversion
39
+
40
+ - **Quantization:** q4f16
41
+ - **Runtime:** Transformers.js via WebGPU
42
+ - **Files:** `onnx/model_q4f16.onnx`, external data files, tokenizer, and chat template
43
+
44
+ ## Sources And Attribution
45
+
46
+ Training data was synthesized from the same investigative journalism corpus as the Gemma variants. It includes [tomvaillant/osint-tool-database](https://huggingface.co/datasets/tomvaillant/osint-tool-database), Indicator Media briefing tools, Buried Signals investigative skills, GIJN manuals, Bellingcat guides, Verification Handbook 3, SPJ Code of Ethics, RCFP resources, and public investigative manuals from UNESCO, Al Jazeera Media Institute, CiFAR, CIPE, and EJF/TEMPO Institute.
47
+
48
+ Full attribution is maintained in the training repository as `SOURCES.md`.
49
+
50
+ ## Intended Use
51
+
52
+ Use for local/browser investigative assistance. Generated findings, URLs, and legal conclusions require independent verification before reporting or operational use.
added_tokens.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "</think>": 151668,
3
+ "</tool_call>": 151658,
4
+ "</tool_response>": 151666,
5
+ "<think>": 151667,
6
+ "<tool_call>": 151657,
7
+ "<tool_response>": 151665,
8
+ "<|PAD_TOKEN|>": 151669,
9
+ "<|box_end|>": 151649,
10
+ "<|box_start|>": 151648,
11
+ "<|endoftext|>": 151643,
12
+ "<|file_sep|>": 151664,
13
+ "<|fim_middle|>": 151660,
14
+ "<|fim_pad|>": 151662,
15
+ "<|fim_prefix|>": 151659,
16
+ "<|fim_suffix|>": 151661,
17
+ "<|im_end|>": 151645,
18
+ "<|im_start|>": 151644,
19
+ "<|image_pad|>": 151655,
20
+ "<|object_ref_end|>": 151647,
21
+ "<|object_ref_start|>": 151646,
22
+ "<|quad_end|>": 151651,
23
+ "<|quad_start|>": 151650,
24
+ "<|repo_name|>": 151663,
25
+ "<|video_pad|>": 151656,
26
+ "<|vision_end|>": 151653,
27
+ "<|vision_pad|>": 151654,
28
+ "<|vision_start|>": 151652
29
+ }
chat_template.jinja ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if tools %}
2
+ {{- '<|im_start|>system\n' }}
3
+ {%- if messages[0].role == 'system' %}
4
+ {{- messages[0].content + '\n\n' }}
5
+ {%- endif %}
6
+ {{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
7
+ {%- for tool in tools %}
8
+ {{- "\n" }}
9
+ {{- tool | tojson }}
10
+ {%- endfor %}
11
+ {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
12
+ {%- else %}
13
+ {%- if messages[0].role == 'system' %}
14
+ {{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
15
+ {%- endif %}
16
+ {%- endif %}
17
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
18
+ {%- for forward_message in messages %}
19
+ {%- set index = (messages|length - 1) - loop.index0 %}
20
+ {%- set message = messages[index] %}
21
+ {%- set tool_start = '<tool_response>' %}
22
+ {%- set tool_start_length = tool_start|length %}
23
+ {%- set start_of_message = message.content[:tool_start_length] %}
24
+ {%- set tool_end = '</tool_response>' %}
25
+ {%- set tool_end_length = tool_end|length %}
26
+ {%- set start_pos = (message.content|length) - tool_end_length %}
27
+ {%- if start_pos < 0 %}
28
+ {%- set start_pos = 0 %}
29
+ {%- endif %}
30
+ {%- set end_of_message = message.content[start_pos:] %}
31
+ {%- if ns.multi_step_tool and message.role == "user" and not(start_of_message == tool_start and end_of_message == tool_end) %}
32
+ {%- set ns.multi_step_tool = false %}
33
+ {%- set ns.last_query_index = index %}
34
+ {%- endif %}
35
+ {%- endfor %}
36
+ {%- for message in messages %}
37
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
38
+ {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
39
+ {%- elif message.role == "assistant" %}
40
+ {%- set content = message.content %}
41
+ {%- set reasoning_content = '' %}
42
+ {%- if message.reasoning_content is defined and message.reasoning_content is not none %}
43
+ {%- set reasoning_content = message.reasoning_content %}
44
+ {%- else %}
45
+ {%- if '</think>' in message.content %}
46
+ {%- set content = (message.content.split('</think>')|last).lstrip('\n') %}
47
+ {%- set reasoning_content = (message.content.split('</think>')|first).rstrip('\n') %}
48
+ {%- set reasoning_content = (reasoning_content.split('<think>')|last).lstrip('\n') %}
49
+ {%- endif %}
50
+ {%- endif %}
51
+ {%- if loop.index0 > ns.last_query_index %}
52
+ {%- if loop.last or (not loop.last and reasoning_content) %}
53
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
54
+ {%- else %}
55
+ {{- '<|im_start|>' + message.role + '\n' + content }}
56
+ {%- endif %}
57
+ {%- else %}
58
+ {{- '<|im_start|>' + message.role + '\n' + content }}
59
+ {%- endif %}
60
+ {%- if message.tool_calls %}
61
+ {%- for tool_call in message.tool_calls %}
62
+ {%- if (loop.first and content) or (not loop.first) %}
63
+ {{- '\n' }}
64
+ {%- endif %}
65
+ {%- if tool_call.function %}
66
+ {%- set tool_call = tool_call.function %}
67
+ {%- endif %}
68
+ {{- '<tool_call>\n{"name": "' }}
69
+ {{- tool_call.name }}
70
+ {{- '", "arguments": ' }}
71
+ {%- if tool_call.arguments is string %}
72
+ {{- tool_call.arguments }}
73
+ {%- else %}
74
+ {{- tool_call.arguments | tojson }}
75
+ {%- endif %}
76
+ {{- '}\n</tool_call>' }}
77
+ {%- endfor %}
78
+ {%- endif %}
79
+ {{- '<|im_end|>\n' }}
80
+ {%- elif message.role == "tool" %}
81
+ {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
82
+ {{- '<|im_start|>user' }}
83
+ {%- endif %}
84
+ {{- '\n<tool_response>\n' }}
85
+ {{- message.content }}
86
+ {{- '\n</tool_response>' }}
87
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
88
+ {{- '<|im_end|>\n' }}
89
+ {%- endif %}
90
+ {%- endif %}
91
+ {%- endfor %}
92
+ {%- if add_generation_prompt %}
93
+ {{- '<|im_start|>assistant\n' }}
94
+ {%- if enable_thinking is defined and enable_thinking is false %}
95
+ {{- '<think>\n\n</think>\n\n' }}
96
+ {%- endif %}
97
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Qwen3ForCausalLM"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "bos_token_id": null,
8
+ "torch_dtype": "bfloat16",
9
+ "eos_token_id": 151645,
10
+ "head_dim": 128,
11
+ "hidden_act": "silu",
12
+ "hidden_size": 2560,
13
+ "initializer_range": 0.02,
14
+ "intermediate_size": 9728,
15
+ "layer_types": [
16
+ "full_attention",
17
+ "full_attention",
18
+ "full_attention",
19
+ "full_attention",
20
+ "full_attention",
21
+ "full_attention",
22
+ "full_attention",
23
+ "full_attention",
24
+ "full_attention",
25
+ "full_attention",
26
+ "full_attention",
27
+ "full_attention",
28
+ "full_attention",
29
+ "full_attention",
30
+ "full_attention",
31
+ "full_attention",
32
+ "full_attention",
33
+ "full_attention",
34
+ "full_attention",
35
+ "full_attention",
36
+ "full_attention",
37
+ "full_attention",
38
+ "full_attention",
39
+ "full_attention",
40
+ "full_attention",
41
+ "full_attention",
42
+ "full_attention",
43
+ "full_attention",
44
+ "full_attention",
45
+ "full_attention",
46
+ "full_attention",
47
+ "full_attention",
48
+ "full_attention",
49
+ "full_attention",
50
+ "full_attention",
51
+ "full_attention"
52
+ ],
53
+ "max_position_embeddings": 40960,
54
+ "max_window_layers": 36,
55
+ "model_type": "qwen3",
56
+ "num_attention_heads": 32,
57
+ "num_hidden_layers": 36,
58
+ "num_key_value_heads": 8,
59
+ "pad_token_id": 151669,
60
+ "rms_norm_eps": 1e-06,
61
+ "rope_parameters": {
62
+ "rope_theta": 1000000,
63
+ "rope_type": "default"
64
+ },
65
+ "sliding_window": null,
66
+ "tie_word_embeddings": true,
67
+ "unsloth_fixed": true,
68
+ "unsloth_version": "2026.4.4",
69
+ "use_cache": true,
70
+ "use_sliding_window": false,
71
+ "vocab_size": 151936,
72
+ "transformers.js_config": {
73
+ "dtype": "q4f16",
74
+ "kv_cache_dtype": {
75
+ "q4f16": "float16"
76
+ },
77
+ "use_external_data_format": {
78
+ "model_q4f16.onnx": 2
79
+ }
80
+ }
81
+ }
generation_config.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "eos_token_id": 151645,
4
+ "pad_token_id": 151669,
5
+ "transformers_version": "4.57.6"
6
+ }
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
onnx/model_q4f16.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bebdbe2f5d4fb822cb090ccc55f4470e90889532867372724d3684cb4d767282
3
+ size 2583451
onnx/model_q4f16.onnx_data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f9dcb7bae0caa725a641e90602a4dfe72abc1769f9d5d2560007540a8b03c9bf
3
+ size 1899530240
onnx/model_q4f16.onnx_data_1 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:52b7043779ea76cca528a047130c14319ae3bfcf4f6dbb32930347d32e936d84
3
+ size 1141302272
special_tokens_map.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "eos_token": {
3
+ "content": "<|im_end|>",
4
+ "lstrip": false,
5
+ "normalized": false,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "pad_token": {
10
+ "content": "<|PAD_TOKEN|>",
11
+ "lstrip": false,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ }
16
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d7430e9138b76e93fb6f93462394d236b411111aef53cb421ba97d2691040cca
3
+ size 11423114
tokenizer_config.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": null,
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|im_end|>",
7
+ "errors": "replace",
8
+ "is_local": false,
9
+ "model_max_length": 40960,
10
+ "pad_token": "<|PAD_TOKEN|>",
11
+ "padding_side": "right",
12
+ "split_special_tokens": false,
13
+ "tokenizer_class": "Qwen2Tokenizer",
14
+ "unk_token": null,
15
+ "chat_template": "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].role == 'system' %}\n {{- messages[0].content + '\\n\\n' }}\n {%- endif %}\n {{- \"# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0].content + '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}\n{%- for forward_message in messages %}\n {%- set index = (messages|length - 1) - loop.index0 %}\n {%- set message = messages[index] %}\n {%- set tool_start = '<tool_response>' %}\n {%- set tool_start_length = tool_start|length %}\n {%- set start_of_message = message.content[:tool_start_length] %}\n {%- set tool_end = '</tool_response>' %}\n {%- set tool_end_length = tool_end|length %}\n {%- set start_pos = (message.content|length) - tool_end_length %}\n {%- if start_pos < 0 %}\n {%- set start_pos = 0 %}\n {%- endif %}\n {%- set end_of_message = message.content[start_pos:] %}\n {%- if ns.multi_step_tool and message.role == \"user\" and not(start_of_message == tool_start and end_of_message == tool_end) %}\n {%- set ns.multi_step_tool = false %}\n {%- set ns.last_query_index = index %}\n {%- endif %}\n{%- endfor %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {%- set content = message.content %}\n {%- set reasoning_content = '' %}\n {%- if message.reasoning_content is defined and message.reasoning_content is not none %}\n {%- set reasoning_content = message.reasoning_content %}\n {%- else %}\n {%- if '</think>' in message.content %}\n {%- set content = (message.content.split('</think>')|last).lstrip('\\n') %}\n {%- set reasoning_content = (message.content.split('</think>')|first).rstrip('\\n') %}\n {%- set reasoning_content = (reasoning_content.split('<think>')|last).lstrip('\\n') %}\n {%- endif %}\n {%- endif %}\n {%- if loop.index0 > ns.last_query_index %}\n {%- if loop.last or (not loop.last and reasoning_content) %}\n {{- '<|im_start|>' + message.role + '\\n<think>\\n' + reasoning_content.strip('\\n') + '\\n</think>\\n\\n' + content.lstrip('\\n') }}\n {%- else %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- endif %}\n {%- else %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- endif %}\n {%- if message.tool_calls %}\n {%- for tool_call in message.tool_calls %}\n {%- if (loop.first and content) or (not loop.first) %}\n {{- '\\n' }}\n {%- endif %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {%- if tool_call.arguments is string %}\n {{- tool_call.arguments }}\n {%- else %}\n {{- tool_call.arguments | tojson }}\n {%- endif %}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n {%- if enable_thinking is defined and enable_thinking is false %}\n {{- '<think>\\n\\n</think>\\n\\n' }}\n {%- endif %}\n{%- endif %}"
16
+ }
vocab.json ADDED
The diff for this file is too large to render. See raw diff