Skip to content

Commit a10dbef

Browse files
authored
✨enhancement: Model sources should not be hard coded from frontend (#5270)
1 parent b5bdf3c commit a10dbef

File tree

3 files changed

+198
-6
lines changed

3 files changed

+198
-6
lines changed

extensions/model-extension/resources/default.json

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,187 @@
11
[
2+
{
3+
"author": "Menlo",
4+
"id": "Menlo/Jan-nano",
5+
"metadata": {
6+
"_id": "68492cd9cada68b1d11ca1bd",
7+
"author": "Menlo",
8+
"cardData": {
9+
"license": "apache-2.0",
10+
"pipeline_tag": "text-generation"
11+
},
12+
"createdAt": "2025-06-11T07:14:33.000Z",
13+
"description": "---\nlicense: apache-2.0\npipeline_tag: text-generation\n---\n# Jan Nano\n\n\n\n![image/png](https://cdn-uploads.huggingface.co/production/uploads/657a81129ea9d52e5cbd67f7/YQci8jiHjAAFpXWYOadrU.png)\n\n## Overview\n\nJan Nano is a fine-tuned language model built on top of the Qwen3 architecture. Developed as part of the Jan ecosystem, it balances compact size and extended context length, making it ideal for efficient, high-quality text generation in local or embedded environments.\n\n## Features\n\n- **Tool Use**: Excellent function calling and tool integration\n- **Research**: Enhanced research and information processing capabilities\n- **Small Model**: VRAM efficient for local deployment\n\n## Use it with Jan (UI)\n\n1. Install **Jan** using [Quickstart](https://jan.ai/docs/quickstart)",
14+
"disabled": false,
15+
"downloads": 1434,
16+
"gated": false,
17+
"gguf": {
18+
"architecture": "qwen3",
19+
"bos_token": "<|endoftext|>",
20+
"chat_template": "{%- if tools %} {{- '<|im_start|>system\\n' }} {%- if messages[0].role == 'system' %} {{- messages[0].content + '\\n\\n' }} {%- endif %} {{- \"# 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>\" }} {%- for tool in tools %} {{- \"\\n\" }} {{- tool | tojson }} {%- endfor %} {{- \"\\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\" }} {%- else %} {%- if messages[0].role == 'system' %} {{- '<|im_start|>system\\n' + messages[0].content + '<|im_end|>\\n' }} {%- endif %} {%- endif %} {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %} {%- for message in messages[::-1] %} {%- set index = (messages|length - 1) - loop.index0 %} {%- if ns.multi_step_tool and message.role == \"user\" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %} {%- set ns.multi_step_tool = false %} {%- set ns.last_query_index = index %} {%- endif %} {%- endfor %} {%- for message in messages %} {%- if message.content is string %} {%- set content = message.content %} {%- else %} {%- set content = '' %} {%- endif %} {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) %} {{- '<|im_start|>' + message.role + '\\n' + content + '<|im_end|>' + '\\n' }} {%- elif message.role == \"assistant\" %} {%- set reasoning_content = '' %} {%- if message.reasoning_content is string %} {%- set reasoning_content = message.reasoning_content %} {%- else %} {%- if '</think>' in content %} {%- set reasoning_content = content.split('</think>')[0].rstrip('\\n').split('<think>')[-1].lstrip('\\n') %} {%- set content = content.split('</think>')[-1].lstrip('\\n') %} {%- endif %} {%- endif %} {%- if loop.index0 > ns.last_query_index %} {%- if loop.last or (not loop.last and reasoning_content) %} {{- '<|im_start|>' + message.role + '\\n<think>\\n' + reasoning_content.strip('\\n') + '\\n</think>\\n\\n' + content.lstrip('\\n') }} {%- else %} {{- '<|im_start|>' + message.role + '\\n' + content }} {%- endif %} {%- else %} {{- '<|im_start|>' + message.role + '\\n' + content }} {%- endif %} {%- if message.tool_calls %} {%- for tool_call in message.tool_calls %} {%- if (loop.first and content) or (not loop.first) %} {{- '\\n' }} {%- endif %} {%- if tool_call.function %} {%- set tool_call = tool_call.function %} {%- endif %} {{- '<tool_call>\\n{\"name\": \"' }} {{- tool_call.name }} {{- '\", \"arguments\": ' }} {%- if tool_call.arguments is string %} {{- tool_call.arguments }} {%- else %} {{- tool_call.arguments | tojson }} {%- endif %} {{- '}\\n</tool_call>' }} {%- endfor %} {%- endif %} {{- '<|im_end|>\\n' }} {%- elif message.role == \"tool\" %} {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %} {{- '<|im_start|>user' }} {%- endif %} {{- '\\n<tool_response>\\n' }} {{- content }} {{- '\\n</tool_response>' }} {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %} {{- '<|im_end|>\\n' }} {%- endif %} {%- endif %} {%- endfor %} {%- if add_generation_prompt %} {{- '<|im_start|>assistant\\n' }} {{- '<think>\\n\\n</think>\\n\\n' }} {%- endif %}",
21+
"context_length": 40960,
22+
"eos_token": "<|im_end|>",
23+
"quantize_imatrix_file": "imatrix.dat",
24+
"total": 4022468096
25+
},
26+
"id": "Menlo/Jan-nano",
27+
"lastModified": "2025-06-13T16:57:55.000Z",
28+
"likes": 3,
29+
"model-index": null,
30+
"modelId": "Menlo/Jan-nano",
31+
"pipeline_tag": "text-generation",
32+
"private": false,
33+
"sha": "a04aab0878648d8f284c63a52664a482ead16f06",
34+
"siblings": [
35+
{
36+
"rfilename": ".gitattributes",
37+
"size": 3460
38+
},
39+
{
40+
"rfilename": "README.md",
41+
"size": 776
42+
},
43+
{
44+
"rfilename": "jan-nano-4b-iQ4_XS.gguf",
45+
"size": 2270750400
46+
},
47+
{
48+
"rfilename": "jan-nano-4b-Q3_K_L.gguf",
49+
"size": 2239784384
50+
},
51+
{
52+
"rfilename": "jan-nano-4b-Q3_K_M.gguf",
53+
"size": 2075616704
54+
},
55+
{
56+
"rfilename": "jan-nano-4b-Q3_K_S.gguf",
57+
"size": 1886995904
58+
},
59+
{
60+
"rfilename": "jan-nano-4b-Q4_0.gguf",
61+
"size": 2369545664
62+
},
63+
{
64+
"rfilename": "jan-nano-4b-Q4_1.gguf",
65+
"size": 2596627904
66+
},
67+
{
68+
"rfilename": "jan-nano-4b-Q4_K_M.gguf",
69+
"size": 2497279424
70+
},
71+
{
72+
"rfilename": "jan-nano-4b-Q4_K_S.gguf",
73+
"size": 2383308224
74+
},
75+
{
76+
"rfilename": "jan-nano-4b-Q5_0.gguf",
77+
"size": 2823710144
78+
},
79+
{
80+
"rfilename": "jan-nano-4b-Q5_1.gguf",
81+
"size": 3050792384
82+
},
83+
{
84+
"rfilename": "jan-nano-4b-Q5_K_M.gguf",
85+
"size": 2889512384
86+
},
87+
{
88+
"rfilename": "jan-nano-4b-Q5_K_S.gguf",
89+
"size": 2823710144
90+
},
91+
{
92+
"rfilename": "jan-nano-4b-Q6_K.gguf",
93+
"size": 3306259904
94+
},
95+
{
96+
"rfilename": "jan-nano-4b-Q8_0.gguf",
97+
"size": 4280403904
98+
}
99+
],
100+
"spaces": [],
101+
"tags": [
102+
"gguf",
103+
"text-generation",
104+
"license:apache-2.0",
105+
"endpoints_compatible",
106+
"region:us",
107+
"imatrix",
108+
"conversational"
109+
],
110+
"usedStorage": 93538518464,
111+
"widgetData": [
112+
{
113+
"text": "Hi, what can you help me with?"
114+
},
115+
{
116+
"text": "What is 84 * 3 / 2?"
117+
},
118+
{
119+
"text": "Tell me an interesting fact about the universe!"
120+
},
121+
{
122+
"text": "Explain quantum computing in simple terms."
123+
}
124+
]
125+
},
126+
"models": [
127+
{
128+
"id": "Menlo:Jan-nano:jan-nano-4b-iQ4_XS.gguf",
129+
"size": 2270750400
130+
},
131+
{
132+
"id": "Menlo:Jan-nano:jan-nano-4b-Q3_K_L.gguf",
133+
"size": 2239784384
134+
},
135+
{
136+
"id": "Menlo:Jan-nano:jan-nano-4b-Q3_K_M.gguf",
137+
"size": 2075616704
138+
},
139+
{
140+
"id": "Menlo:Jan-nano:jan-nano-4b-Q3_K_S.gguf",
141+
"size": 1886995904
142+
},
143+
{
144+
"id": "Menlo:Jan-nano:jan-nano-4b-Q4_0.gguf",
145+
"size": 2369545664
146+
},
147+
{
148+
"id": "Menlo:Jan-nano:jan-nano-4b-Q4_1.gguf",
149+
"size": 2596627904
150+
},
151+
{
152+
"id": "Menlo:Jan-nano:jan-nano-4b-Q4_K_M.gguf",
153+
"size": 2497279424
154+
},
155+
{
156+
"id": "Menlo:Jan-nano:jan-nano-4b-Q4_K_S.gguf",
157+
"size": 2383308224
158+
},
159+
{
160+
"id": "Menlo:Jan-nano:jan-nano-4b-Q5_0.gguf",
161+
"size": 2823710144
162+
},
163+
{
164+
"id": "Menlo:Jan-nano:jan-nano-4b-Q5_1.gguf",
165+
"size": 3050792384
166+
},
167+
{
168+
"id": "Menlo:Jan-nano:jan-nano-4b-Q5_K_M.gguf",
169+
"size": 2889512384
170+
},
171+
{
172+
"id": "Menlo:Jan-nano:jan-nano-4b-Q5_K_S.gguf",
173+
"size": 2823710144
174+
},
175+
{
176+
"id": "Menlo:Jan-nano:jan-nano-4b-Q6_K.gguf",
177+
"size": 3306259904
178+
},
179+
{
180+
"id": "Menlo:Jan-nano:jan-nano-4b-Q8_0.gguf",
181+
"size": 4280403904
182+
}
183+
]
184+
},
2185
{
3186
"author": "PrimeIntellect",
4187
"id": "cortexso/intellect-2",

extensions/model-extension/src/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ type Data<T> = {
2525
data: T[]
2626
}
2727

28+
/**
29+
* Defaul mode sources
30+
*/
31+
const defaultModelSources = ['Menlo/Jan-nano']
32+
2833
/**
2934
* A extension for models
3035
*/
@@ -396,14 +401,19 @@ export default class JanModelExtension extends ModelExtension {
396401
fetchModelsHub = async () => {
397402
const models = await this.fetchModels()
398403

404+
defaultModelSources.forEach((model) => {
405+
this.addSource(model).catch((e) => {
406+
console.debug(`Failed to add default model source ${model}:`, e)
407+
})
408+
})
399409
return this.apiInstance()
400410
.then((api) =>
401411
api
402412
.get('v1/models/hub?author=cortexso&tag=cortex.cpp')
403413
.json<Data<string>>()
404414
.then(async (e) => {
405415
await Promise.all(
406-
e.data?.map((model) => {
416+
[...(e.data ?? []), ...defaultModelSources].map((model) => {
407417
if (
408418
!models.some(
409419
(e) => 'modelSource' in e && e.modelSource === model

web-app/src/services/models.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import { ExtensionManager } from '@/lib/extension'
33
import { normalizeProvider } from '@/lib/models'
4-
import { hardcodedModel } from '@/utils/models'
54
import { EngineManager, ExtensionTypeEnum, ModelExtension } from '@janhq/core'
65
import { Model as CoreModel } from '@janhq/core'
76

@@ -24,7 +23,7 @@ export const fetchModelSources = async (): Promise<any[]> => {
2423
ExtensionTypeEnum.Model
2524
)
2625

27-
if (!extension) return [hardcodedModel]
26+
if (!extension) return []
2827

2928
try {
3029
const sources = await extension.getSources()
@@ -34,10 +33,10 @@ export const fetchModelSources = async (): Promise<any[]> => {
3433
}))
3534

3635
// Prepend the hardcoded model to the sources
37-
return [hardcodedModel, ...mappedSources]
36+
return [...mappedSources]
3837
} catch (error) {
3938
console.error('Failed to fetch model sources:', error)
40-
return [hardcodedModel]
39+
return []
4140
}
4241
}
4342

@@ -51,7 +50,7 @@ export const fetchModelHub = async (): Promise<any[]> => {
5150
?.fetchModelsHub()
5251

5352
// Prepend the hardcoded model to the hub data
54-
return hubData ? [hardcodedModel, ...hubData] : [hardcodedModel]
53+
return hubData ? [...hubData] : []
5554
}
5655

5756
/**

0 commit comments

Comments
 (0)