ryugyosoft commited on
Commit
0d57fed
·
verified ·
1 Parent(s): 3421544

onw 1.2.2: API default sampling setting (for apps that send none), chat starts from custom values

Browse files
CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@
5
  日付は日本時間です。各版は Hugging Face の `ryugyosoft/onw` にタグ(`v0.4.2` 以降)付きで残っています。
6
  onw ウィンドウは、新しい版があるとこのファイルから変更点を表示します(`## v…` の見出しで区切っています)。
7
 
 
 
 
 
 
 
8
  ## v1.2.1 (2026-10-04)
9
  - Gemma:設定の「64 トークン処理」を切り替えるたびに、NPU 向けのコンパイル(数分)をやり直していた問題を修正。スライディング
10
  ウィンドウ層のリングの長さが「読み込んだ中で最大のブロック」で決まっていたため、64 トークン処理のオン/オフで 1 トークン用・
 
5
  日付は日本時間です。各版は Hugging Face の `ryugyosoft/onw` にタグ(`v0.4.2` 以降)付きで残っています。
6
  onw ウィンドウは、新しい版があるとこのファイルから変更点を表示します(`## v…` の見出しで区切っています)。
7
 
8
+ ## v1.2.2 (2026-10-04)
9
+ - 設定の「サーバーと API」に「API の既定の生成設定」を追加。温度・Top P などを送らないアプリ(OpenCode など)のリクエストに、
10
+ 「貪欲(温度 0。これまでどおり、既定)」「モデルの推奨値(チャット画面と同じ。Qwen 系は思考時に思考用の値)」「自分で指定」の
11
+ どれを使うかを選べます。アプリが送った値はそのまま使います。「自分で指定」にすると、チャット画面の初期値もその値になります
12
+ (「(設定の値)」と表示)。
13
+
14
  ## v1.2.1 (2026-10-04)
15
  - Gemma:設定の「64 トークン処理」を切り替えるたびに、NPU 向けのコンパイル(数分)をやり直していた問題を修正。スライディング
16
  ウィンドウ層のリングの長さが「読み込んだ中で最大のブロック」で決まっていたため、64 トークン処理のオン/オフで 1 トークン用・
CHANGELOG_en.md CHANGED
@@ -4,6 +4,12 @@
4
 
5
  Dates are JST. Every version is on Hugging Face `ryugyosoft/onw`, tagged from `v0.4.2` on.
6
 
 
 
 
 
 
 
7
  ## v1.2.1 (2026-10-04)
8
  - Gemma: switching "64-token blocks" in the settings compiled the model for the NPU again (minutes) every time. The
9
  sliding-window layers' ring length followed the largest block loaded, so turning the 64-token blocks on or off changed
 
4
 
5
  Dates are JST. Every version is on Hugging Face `ryugyosoft/onw`, tagged from `v0.4.2` on.
6
 
7
+ ## v1.2.2 (2026-10-04)
8
+ - Settings, Server and API: "API default sampling" for requests that send no temperature, Top P etc. (OpenCode, ...):
9
+ greedy (temperature 0, as before, the default), the model's recommended values (as in the chat; the Qwen family
10
+ switches to its thinking values when thinking) or custom values. Values an app sends are always used. With custom
11
+ values the chat page starts from them too ("(the values from the settings)").
12
+
13
  ## v1.2.1 (2026-10-04)
14
  - Gemma: switching "64-token blocks" in the settings compiled the model for the NPU again (minutes) every time. The
15
  sliding-window layers' ring length followed the largest block loaded, so turning the 64-token blocks on or off changed
MANUAL.md CHANGED
@@ -143,6 +143,7 @@
143
  | コンテキスト長 | 会話で使える最大の長さ。KV キャッシュは実際に使った分だけメモリを使うので、大きくしても短い会話は遅くなりません。変えても再コンパイルはありません |
144
  | LAN からの接続を許可する | 他の PC から API を使えるようにします(API キーの設定をおすすめします) |
145
  | API で思考の指定がないリクエストも考えてから答える | `enable_thinking` を送らないエージェントのアプリ向け。回答は遅くなります |
 
146
 
147
  ### 速さとメモリ
148
 
 
143
  | コンテキスト長 | 会話で使える最大の長さ。KV キャッシュは実際に使った分だけメモリを使うので、大きくしても短い会話は遅くなりません。変えても再コンパイルはありません |
144
  | LAN からの接続を許可する | 他の PC から API を使えるようにします(API キーの設定をおすすめします) |
145
  | API で思考の指定がないリクエストも考えてから答える | `enable_thinking` を送らないエージェントのアプリ向け。回答は遅くなります |
146
+ | API の既定の生成設定 | 温度・Top P などを送らないアプリ(OpenCode など)のリクエストに使う値。「貪欲(温度 0)」(既定)、「モデルの推奨値」(チャット画面と同じ。Qwen 系は思考時に思考用の値)、「自分で指定」。アプリが送った値はそのまま使います。「自分で指定」にすると、チャット画面の初期値もその値になります |
147
 
148
  ### 速さとメモリ
149
 
MANUAL_en.md CHANGED
@@ -152,6 +152,7 @@ applies them ("Discard" throws them away). For settings that need a server resta
152
  | Context length | The longest a conversation can get. The KV cache only uses memory for what is used, so a larger value does not slow short conversations; changing it needs no recompile |
153
  | Allow connections from the LAN | Lets other PCs use the API (setting an API key is recommended) |
154
  | Think before answering API requests that do not say | For agent apps that never send `enable_thinking`. Answers take longer |
 
155
 
156
  ### Speed and memory
157
 
 
152
  | Context length | The longest a conversation can get. The KV cache only uses memory for what is used, so a larger value does not slow short conversations; changing it needs no recompile |
153
  | Allow connections from the LAN | Lets other PCs use the API (setting an API key is recommended) |
154
  | Think before answering API requests that do not say | For agent apps that never send `enable_thinking`. Answers take longer |
155
+ | API default sampling | The values for requests that send no temperature, Top P etc. (OpenCode, ...): greedy (temperature 0, the default), the model's recommended values (as in the chat; the Qwen family switches to its thinking values when thinking) or custom. Values an app sends are always used. With custom values the chat page starts from them too |
156
 
157
  ### Speed and memory
158
 
onw/__init__.py CHANGED
@@ -2,4 +2,4 @@
2
 
3
  A small engine that runs LLMs entirely on the Intel NPU: static segment graphs + host-managed state.
4
  """
5
- __version__ = "1.2.1"
 
2
 
3
  A small engine that runs LLMs entirely on the Intel NPU: static segment graphs + host-managed state.
4
  """
5
+ __version__ = "1.2.2"
onw/app.py CHANGED
@@ -84,7 +84,8 @@ STREAMABLE = {
84
  }
85
  DEFAULTS = {"model": "", "port": 8000, "lan": False, "api_key": "", "context": 32768, "device": "NPU",
86
  "autostart_server": True, "login_start": False, "models_dir": "", "model_folders": [], "pld": True, "big_blocks": "auto", "npuw": "auto", "expert_gb": "auto", "update_check": True, "mem_saver": False, "mem_saver_strict": False, "think_default": False,
87
- "lang": "auto", "npuw_moe": False, "npuw_async": False, "cache_size": False}
 
88
  CONTEXTS = ["1024", "2048", "4096", "8192", "16384", "32768", "65536", "131072", "262144"]
89
 
90
 
@@ -651,6 +652,14 @@ def _start_server(cfg):
651
  env["ONW_EXPERT_GB"] = str(budget)
652
  if cfg.get("think_default"): # API requests that say nothing about thinking: think
653
  env["ONW_THINK_DEFAULT"] = "1"
 
 
 
 
 
 
 
 
654
  if cfg.get("mem_saver") and saver_applies(model, cfg): # memory-saving mode: one block size at a time
655
  env["ONW_LAZY16"] = "1"
656
  if cfg.get("mem_saver_strict"): # strict: never both block sizes at once (answers wait)
 
84
  }
85
  DEFAULTS = {"model": "", "port": 8000, "lan": False, "api_key": "", "context": 32768, "device": "NPU",
86
  "autostart_server": True, "login_start": False, "models_dir": "", "model_folders": [], "pld": True, "big_blocks": "auto", "npuw": "auto", "expert_gb": "auto", "update_check": True, "mem_saver": False, "mem_saver_strict": False, "think_default": False,
87
+ "lang": "auto", "npuw_moe": False, "npuw_async": False, "cache_size": False,
88
+ "api_sampling": "greedy", "api_temperature": 0.7, "api_top_p": 0.95, "api_top_k": 20, "api_presence": 0.0}
89
  CONTEXTS = ["1024", "2048", "4096", "8192", "16384", "32768", "65536", "131072", "262144"]
90
 
91
 
 
652
  env["ONW_EXPERT_GB"] = str(budget)
653
  if cfg.get("think_default"): # API requests that say nothing about thinking: think
654
  env["ONW_THINK_DEFAULT"] = "1"
655
+ # API requests that send no sampling values (agent apps like OpenCode): greedy, the model's recommendation or set
656
+ if cfg.get("api_sampling") == "recommended":
657
+ env["ONW_API_SAMPLING"] = "recommended"
658
+ elif cfg.get("api_sampling") == "custom":
659
+ env["ONW_API_SAMPLING"] = json.dumps({"temperature": float(cfg.get("api_temperature", 0.7)),
660
+ "top_p": float(cfg.get("api_top_p", 0.95)),
661
+ "top_k": int(cfg.get("api_top_k", 0)),
662
+ "presence_penalty": float(cfg.get("api_presence", 0.0))})
663
  if cfg.get("mem_saver") and saver_applies(model, cfg): # memory-saving mode: one block size at a time
664
  env["ONW_LAZY16"] = "1"
665
  if cfg.get("mem_saver_strict"): # strict: never both block sizes at once (answers wait)
onw/i18n.json CHANGED
@@ -10,6 +10,7 @@
10
  "35B MoE を半分に刈り込み": "The 35B MoE pruned by half",
11
  "64 トークン処理": "64-token blocks",
12
  "API で思考の指定がないリクエストも考えてから答える": "Think before answering API requests that do not say",
 
13
  "API キー(任意)": "API key (optional)",
14
  "Hugging Face に接続できません: {0}": "Cannot reach Hugging Face: {0}",
15
  "IBM の 8B。思考とツール呼び出し・コーディング向け": "IBM's 8B, for thinking, tool calls and coding",
@@ -132,6 +133,7 @@
132
  "メモリ目安": "Memory (est.)",
133
  "メモリ節約モード": "Memory-saving mode",
134
  "モデル": "Models",
 
135
  "モデルは読み込まれていません": "No model loaded",
136
  "モデルをロード": "Load model",
137
  "モデルを削除": "Delete model",
@@ -253,9 +255,11 @@
253
  "次の文章を、元の表現をなるべく使って2文に要約してください。": "Summarize the following text in two sentences, reusing its wording as much as possible.",
254
  "止まった理由: {0}": "Why it stopped: {0}",
255
  "温度": "Temperature",
 
256
  "準備中…": "Preparing…",
257
  "無効": "Off",
258
  "状態": "Status",
 
259
  "生成速度": "Decode speed",
260
  "画像": "Image",
261
  "画像も読めます": "Reads images too",
@@ -267,6 +271,7 @@
267
  "終了(サーバーも停止)": "Quit (stops the server too)",
268
  "結果": "Result",
269
  "繰り返し抑制": "Repetition penalty",
 
270
  "自分で置いたモデル・{0}": "Your own model · {0}",
271
  "要約や書き直しなど、元の文章をなぞる回答が速くなります。": "Speeds up answers that reuse the prompt's text, such as summaries and rewrites.",
272
  "言語": "Language",
@@ -274,6 +279,7 @@
274
  "設定は保存しました。サーバーを新しい設定で今すぐ再起動しますか?(キャンセルしても、次にモデルを読み込むときから反映されます)": "Settings saved. Restart the server with the new settings now? (Cancel keeps them for the next model load.)",
275
  "詳細": "Advanced",
276
  "読み込み中…": "Loading…",
 
277
  "起動時と 1 日ごとに更新を確認する": "Check for updates at startup and daily",
278
  "起動時に前回ロードしたモデルを自動でロードする": "Load the last model automatically at startup",
279
  "追加…": "Add…",
@@ -298,6 +304,7 @@
298
  "(再利用 {0})": " (reused {0})",
299
  "(無題)": "(untitled)",
300
  "(画像)": "(image)",
 
301
  "+ 新しい会話": "+ New conversation"
302
  },
303
  "zh": {
@@ -311,6 +318,7 @@
311
  "35B MoE を半分に刈り込み": "35B MoE 裁剪一半",
312
  "64 トークン処理": "64 token 分块",
313
  "API で思考の指定がないリクエストも考えてから答える": "API 请求未指定思考时也先思考再回答",
 
314
  "API キー(任意)": "API 密钥(可选)",
315
  "Hugging Face に接続できません: {0}": "无法连接 Hugging Face:{0}",
316
  "IBM の 8B。思考とツール呼び出し・コーディング向け": "IBM 的 8B,适合思考、工具调用和编程",
@@ -433,6 +441,7 @@
433
  "メモリ目安": "内存估计",
434
  "メモリ節約モード": "省内存模式",
435
  "モデル": "模型",
 
436
  "モデルは読み込まれていません": "未加载模型",
437
  "モデルをロード": "加载模型",
438
  "モデルを削除": "删除模型",
@@ -554,9 +563,11 @@
554
  "次の文章を、元の表現をなるべく使って2文に要約してください。": "请尽量使用原文的表达,把下面的文字概括成两句话。",
555
  "止まった理由: {0}": "停止原因:{0}",
556
  "温度": "温度",
 
557
  "準備中…": "准备中…",
558
  "無効": "关闭",
559
  "状態": "状态",
 
560
  "生成速度": "生成速度",
561
  "画像": "图像",
562
  "画像も読めます": "也能读图",
@@ -568,6 +579,7 @@
568
  "終了(サーバーも停止)": "退出(同时停止服务器)",
569
  "結果": "结果",
570
  "繰り返し抑制": "重复惩罚",
 
571
  "自分で置いたモデル・{0}": "自行放置的模型・{0}",
572
  "要約や書き直しなど、元の文章をなぞる回答が速くなります。": "让摘要、改写等沿用原文的回答更快。",
573
  "言語": "语言",
@@ -575,6 +587,7 @@
575
  "設定は保存しました。サーバーを新しい設定で今すぐ再起動しますか?(キャンセルしても、次にモデルを読み込むときから反映されます)": "设置已保存。现在用新设置重启服务器?(取消后,下次加载模型时生效)",
576
  "詳細": "高级",
577
  "読み込み中…": "加载中…",
 
578
  "起動時と 1 日ごとに更新を確認する": "启动时及每天检查更新",
579
  "起動時に前回ロードしたモデルを自動でロードする": "启动时自动加载上次的模型",
580
  "追加…": "添加…",
@@ -599,6 +612,7 @@
599
  "(再利用 {0})": "(复用 {0})",
600
  "(無題)": "(无标题)",
601
  "(画像)": "(图片)",
 
602
  "+ 新しい会話": "+ 新对话"
603
  }
604
  }
 
10
  "35B MoE を半分に刈り込み": "The 35B MoE pruned by half",
11
  "64 トークン処理": "64-token blocks",
12
  "API で思考の指定がないリクエストも考えてから答える": "Think before answering API requests that do not say",
13
+ "API の既定の生成設定": "API default sampling",
14
  "API キー(任意)": "API key (optional)",
15
  "Hugging Face に接続できません: {0}": "Cannot reach Hugging Face: {0}",
16
  "IBM の 8B。思考とツール呼び出し・コーディング向け": "IBM's 8B, for thinking, tool calls and coding",
 
133
  "メモリ目安": "Memory (est.)",
134
  "メモリ節約モード": "Memory-saving mode",
135
  "モデル": "Models",
136
+ "モデルの推奨値(チャット画面と同じ)": "The model's recommended values (as in the chat)",
137
  "モデルは読み込まれていません": "No model loaded",
138
  "モデルをロード": "Load model",
139
  "モデルを削除": "Delete model",
 
255
  "次の文章を、元の表現をなるべく使って2文に要約してください。": "Summarize the following text in two sentences, reusing its wording as much as possible.",
256
  "止まった理由: {0}": "Why it stopped: {0}",
257
  "温度": "Temperature",
258
+ "温度などを送らないアプリ(OpenCode など)のリクエストに使います。アプリが送った値はそのまま使います。": "For requests from apps that send no temperature etc. (OpenCode, ...). Values an app sends are always used.",
259
  "準備中…": "Preparing…",
260
  "無効": "Off",
261
  "状態": "Status",
262
+ "生成設定の値が不正です": "Invalid sampling values",
263
  "生成速度": "Decode speed",
264
  "画像": "Image",
265
  "画像も読めます": "Reads images too",
 
271
  "終了(サーバーも停止)": "Quit (stops the server too)",
272
  "結果": "Result",
273
  "繰り返し抑制": "Repetition penalty",
274
+ "自分で指定": "Custom",
275
  "自分で置いたモデル・{0}": "Your own model · {0}",
276
  "要約や書き直しなど、元の文章をなぞる回答が速くなります。": "Speeds up answers that reuse the prompt's text, such as summaries and rewrites.",
277
  "言語": "Language",
 
279
  "設定は保存しました。サーバーを新しい設定で今すぐ再起動しますか?(キャンセルしても、次にモデルを読み込むときから反映されます)": "Settings saved. Restart the server with the new settings now? (Cancel keeps them for the next model load.)",
280
  "詳細": "Advanced",
281
  "読み込み中…": "Loading…",
282
+ "貪欲(温度 0。毎回同じ答え)": "Greedy (temperature 0, the same answer every time)",
283
  "起動時と 1 日ごとに更新を確認する": "Check for updates at startup and daily",
284
  "起動時に前回ロードしたモデルを自動でロードする": "Load the last model automatically at startup",
285
  "追加…": "Add…",
 
304
  "(再利用 {0})": " (reused {0})",
305
  "(無題)": "(untitled)",
306
  "(画像)": "(image)",
307
+ "(設定の値)": "(the values from the settings)",
308
  "+ 新しい会話": "+ New conversation"
309
  },
310
  "zh": {
 
318
  "35B MoE を半分に刈り込み": "35B MoE 裁剪一半",
319
  "64 トークン処理": "64 token 分块",
320
  "API で思考の指定がないリクエストも考えてから答える": "API 请求未指定思考时也先思考再回答",
321
+ "API の既定の生成設定": "API 默认生成设置",
322
  "API キー(任意)": "API 密钥(可选)",
323
  "Hugging Face に接続できません: {0}": "无法连接 Hugging Face:{0}",
324
  "IBM の 8B。思考とツール呼び出し・コーディング向け": "IBM 的 8B,适合思考、工具调用和编程",
 
441
  "メモリ目安": "内存估计",
442
  "メモリ節約モード": "省内存模式",
443
  "モデル": "模型",
444
+ "モデルの推奨値(チャット画面と同じ)": "模型的推荐值(与聊天页面相同)",
445
  "モデルは読み込まれていません": "未加载模型",
446
  "モデルをロード": "加载模型",
447
  "モデルを削除": "删除模型",
 
563
  "次の文章を、元の表現をなるべく使って2文に要約してください。": "请尽量使用原文的表达,把下面的文字概括成两句话。",
564
  "止まった理由: {0}": "停止原因:{0}",
565
  "温度": "温度",
566
+ "温度などを送らないアプリ(OpenCode など)のリクエストに使います。アプリが送った値はそのまま使います。": "用于不发送温度等参数的应用(如 OpenCode)的请求。应用发送的值始终优先。",
567
  "準備中…": "准备中…",
568
  "無効": "关闭",
569
  "状態": "状态",
570
+ "生成設定の値が不正です": "生成设置的值无效",
571
  "生成速度": "生成速度",
572
  "画像": "图像",
573
  "画像も読めます": "也能读图",
 
579
  "終了(サーバーも停止)": "退出(同时停止服务器)",
580
  "結果": "结果",
581
  "繰り返し抑制": "重复惩罚",
582
+ "自分で指定": "自定义",
583
  "自分で置いたモデル・{0}": "自行放置的模型・{0}",
584
  "要約や書き直しなど、元の文章をなぞる回答が速くなります。": "让摘要、改写等沿用原文的回答更快。",
585
  "言語": "语言",
 
587
  "設定は保存しました。サーバーを新しい設定で今すぐ再起動しますか?(キャンセルしても、次にモデルを読み込むときから反映されます)": "设置已保存。现在用新设置重启服务器?(取消后,下次加载模型时生效)",
588
  "詳細": "高级",
589
  "読み込み中…": "加载中…",
590
+ "貪欲(温度 0。毎回同じ答え)": "贪婪(温度 0,每次答案相同)",
591
  "起動時と 1 日ごとに更新を確認する": "启动时及每天检查更新",
592
  "起動時に前回ロードしたモデルを自動でロードする": "启动时自动加载上次的模型",
593
  "追加…": "添加…",
 
612
  "(再利用 {0})": "(复用 {0})",
613
  "(無題)": "(无标题)",
614
  "(画像)": "(图片)",
615
+ "(設定の値)": "(设置中的值)",
616
  "+ 新しい会話": "+ 新对话"
617
  }
618
  }
onw/manager.py CHANGED
@@ -373,11 +373,21 @@ class Manager:
373
  "update_check", "lang", "npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default"):
374
  if k in new:
375
  cfg[k] = new[k]
 
 
 
 
 
 
 
 
 
376
  if ctx != cfg["context"]:
377
  cfg["context_chosen"] = True # (the old 1024 default moves up; a choice stays)
378
  cfg["port"], cfg["context"] = port, ctx
379
  server_keys = ("lan", "api_key", "device", "pld", "big_blocks", "npuw", "expert_gb", "port", "context",
380
- "npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default")
 
381
  app.save_config(cfg)
382
  i18n.current(cfg) # messages switch language right away
383
  try:
 
373
  "update_check", "lang", "npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default"):
374
  if k in new:
375
  cfg[k] = new[k]
376
+ if new.get("api_sampling") in ("greedy", "recommended", "custom"):
377
+ cfg["api_sampling"] = new["api_sampling"]
378
+ try:
379
+ for k, cast, lo, hi in (("api_temperature", float, 0, 2), ("api_top_p", float, 0.01, 1), ("api_top_k", int, 0, 1000),
380
+ ("api_presence", float, 0, 2)):
381
+ if k in new:
382
+ cfg[k] = min(max(cast(float(new[k])), lo), hi)
383
+ except (TypeError, ValueError):
384
+ return {"error": T("生成設定の値が不正です")}
385
  if ctx != cfg["context"]:
386
  cfg["context_chosen"] = True # (the old 1024 default moves up; a choice stays)
387
  cfg["port"], cfg["context"] = port, ctx
388
  server_keys = ("lan", "api_key", "device", "pld", "big_blocks", "npuw", "expert_gb", "port", "context",
389
+ "npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default",
390
+ "api_sampling", "api_temperature", "api_top_p", "api_top_k", "api_presence")
391
  app.save_config(cfg)
392
  i18n.current(cfg) # messages switch language right away
393
  try:
onw/server.py CHANGED
@@ -40,6 +40,7 @@ info = {}
40
  lock = threading.Lock() # one generation at a time on the NPU
41
  WEB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "web")
42
  API_KEY = None # --api-key: required as a Bearer token on /v1
 
43
 
44
 
45
  def oai_error(status, message, type_="invalid_request_error", code=None):
@@ -293,10 +294,15 @@ def gen_args(body):
293
  elif isinstance(choice, dict) and tools: # a named function: offer just that one
294
  name = (choice.get("function") or {}).get("name")
295
  tools = [t for t in tools if (t.get("function") or {}).get("name") == name] or tools
296
- return dict(max_new_tokens=mx, temperature=_num(body, "temperature", 0.0), top_p=_num(body, "top_p", 1.0),
 
 
 
 
 
297
  stop=[stop] if isinstance(stop, str) else list(stop), seed=body.get("seed"), think=think,
298
- top_k=_num(body, "top_k", 0, int), repetition_penalty=_num(body, "repetition_penalty", 1.0),
299
- presence_penalty=_num(body, "presence_penalty", 0.0),
300
  frequency_penalty=_num(body, "frequency_penalty", 0.0), tools=tools)
301
 
302
 
@@ -515,7 +521,7 @@ def recommended_sampling(arch, engine_dir=None):
515
 
516
 
517
  def main(argv=None):
518
- global pipe, MODEL_ID, API_KEY
519
  ap = argparse.ArgumentParser(prog="onw serve", description="chat UI + OpenAI-compatible API on the Intel NPU")
520
  ap.add_argument("model", help="onw model folder, or a Hugging Face repo id (downloaded into ./models)")
521
  ap.add_argument("--host", default="127.0.0.1")
@@ -554,6 +560,18 @@ def main(argv=None):
554
  # greedy Gemma on the NPU can fall into repeating one word (fp16 on the NPU tips a near tie; the same happens with
555
  # any onw version): the pages suggest a repetition penalty, the API default stays 1.0 (OpenAI behaviour)
556
  info.update(arch=arch, rep_hint=1.2 if arch.startswith("gemma") else None, sampling=recommended_sampling(arch, a.engine))
 
 
 
 
 
 
 
 
 
 
 
 
557
  if a.open:
558
  import webbrowser
559
  threading.Timer(1.5, webbrowser.open, [f"http://{'localhost' if a.host in ('0.0.0.0', '127.0.0.1') else a.host}:{a.port}/chat"]).start()
 
40
  lock = threading.Lock() # one generation at a time on the NPU
41
  WEB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "web")
42
  API_KEY = None # --api-key: required as a Bearer token on /v1
43
+ API_SAMPLING = None # sampling for requests that send none (ONW_API_SAMPLING: "recommended" or a JSON of values)
44
 
45
 
46
  def oai_error(status, message, type_="invalid_request_error", code=None):
 
294
  elif isinstance(choice, dict) and tools: # a named function: offer just that one
295
  name = (choice.get("function") or {}).get("name")
296
  tools = [t for t in tools if (t.get("function") or {}).get("name") == name] or tools
297
+ # what the request leaves out: greedy, or the settings' API defaults (API_SAMPLING; thinking has its own set)
298
+ dflt = dict(API_SAMPLING or {})
299
+ if think and isinstance(dflt.get("think"), dict):
300
+ dflt.update(dflt["think"])
301
+ return dict(max_new_tokens=mx, temperature=_num(body, "temperature", dflt.get("temperature", 0.0)),
302
+ top_p=_num(body, "top_p", dflt.get("top_p", 1.0)),
303
  stop=[stop] if isinstance(stop, str) else list(stop), seed=body.get("seed"), think=think,
304
+ top_k=_num(body, "top_k", dflt.get("top_k", 0), int), repetition_penalty=_num(body, "repetition_penalty", 1.0),
305
+ presence_penalty=_num(body, "presence_penalty", dflt.get("presence_penalty", 0.0)),
306
  frequency_penalty=_num(body, "frequency_penalty", 0.0), tools=tools)
307
 
308
 
 
521
 
522
 
523
  def main(argv=None):
524
+ global pipe, MODEL_ID, API_KEY, API_SAMPLING
525
  ap = argparse.ArgumentParser(prog="onw serve", description="chat UI + OpenAI-compatible API on the Intel NPU")
526
  ap.add_argument("model", help="onw model folder, or a Hugging Face repo id (downloaded into ./models)")
527
  ap.add_argument("--host", default="127.0.0.1")
 
560
  # greedy Gemma on the NPU can fall into repeating one word (fp16 on the NPU tips a near tie; the same happens with
561
  # any onw version): the pages suggest a repetition penalty, the API default stays 1.0 (OpenAI behaviour)
562
  info.update(arch=arch, rep_hint=1.2 if arch.startswith("gemma") else None, sampling=recommended_sampling(arch, a.engine))
563
+ want = os.environ.get("ONW_API_SAMPLING")
564
+ if want == "recommended":
565
+ API_SAMPLING = {k: v for k, v in (info["sampling"] or {}).items() if k != "source"} or None
566
+ elif want:
567
+ try:
568
+ API_SAMPLING = json.loads(want)
569
+ except ValueError:
570
+ API_SAMPLING = None
571
+ info.update(api_sampling=API_SAMPLING, api_sampling_mode="recommended" if want == "recommended" else
572
+ "custom" if API_SAMPLING else "greedy")
573
+ if API_SAMPLING:
574
+ print(f"[api] requests without sampling values use {API_SAMPLING}", flush=True)
575
  if a.open:
576
  import webbrowser
577
  threading.Timer(1.5, webbrowser.open, [f"http://{'localhost' if a.host in ('0.0.0.0', '127.0.0.1') else a.host}:{a.port}/chat"]).start()
onw/web/index.html CHANGED
@@ -195,7 +195,10 @@ fetch("/api/info").then(r => r.json()).then(i => {
195
  if (i.always_thinks && $("maxtok").value === "512") $("maxtok").value = "2048";
196
 
197
  if (i.context) $("note").textContent = T("文脈は約{0}トークン", i.context);
198
- if (i.sampling) { // the model's recommended sampling as the starting values
 
 
 
199
  SAMPLING = i.sampling; setSampling(SAMPLING);
200
  $("samphint").textContent = i.sampling.source === "onw" ? T("(onw の推奨値)") : T("(モデルの推奨値)");
201
  $("samphint").style.display = "";
 
195
  if (i.always_thinks && $("maxtok").value === "512") $("maxtok").value = "2048";
196
 
197
  if (i.context) $("note").textContent = T("文脈は約{0}トークン", i.context);
198
+ if (i.api_sampling_mode === "custom" && i.api_sampling) { // the settings' own API values: the chat starts there too
199
+ SAMPLING = i.api_sampling; setSampling(SAMPLING);
200
+ $("samphint").textContent = T("(設定の値)"); $("samphint").style.display = "";
201
+ } else if (i.sampling) { // the model's recommended sampling as the starting values
202
  SAMPLING = i.sampling; setSampling(SAMPLING);
203
  $("samphint").textContent = i.sampling.source === "onw" ? T("(onw の推奨値)") : T("(モデルの推奨値)");
204
  $("samphint").style.display = "";
onw/web/manage.html CHANGED
@@ -217,6 +217,13 @@
217
  <span class="desc">会話で使える最大の長さ。KV キャッシュは実際に使った分だけメモリを使います(1K トークンあたり 8〜80 MB)。変えても再コンパイルはありません。</span></div>
218
  <label class="swrow"><span class="switch"><input type="checkbox" id="f-lan"><i></i></span><span>LAN からの接続を許可する<span class="desc">他の PC から API を使えるようにします。API キーの設定をおすすめします。</span></span></label>
219
  <label class="swrow"><span class="switch"><input type="checkbox" id="f-think_default"><i></i></span><span>API で思考の指定がないリクエストも考えてから答える<span class="desc">enable_thinking を送らないエージェントのアプリ向け。回答は遅くなります。</span></span></label>
 
 
 
 
 
 
 
220
  </div>
221
 
222
  <div class="card set sub" data-sub="speed">
@@ -391,6 +398,9 @@
391
  $("f-device").value = c.device; $("f-big_blocks").value = c.big_blocks || "auto"; $("f-npuw").value = c.npuw || "auto"; $("f-expert_gb").value = String(c.expert_gb || "auto"); $("f-lang").value = c.lang || "auto";
392
  for (const k of ["pld", "autostart_server", "login_start", "lan", "update_check"]) $("f-" + k).checked = c[k] !== false && !!(c[k] ?? true);
393
  for (const k of ["npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default"]) $("f-" + k).checked = !!c[k];
 
 
 
394
  strictState();
395
  const sm = s.saver_models || [];
396
  $("row-mem_saver").style.display = sm.length || c.mem_saver ? "" : "none";
@@ -398,6 +408,8 @@
398
  $("saver-models").textContent = sm.length ? T("この PC で効くモデル:{0}", sm.join(T("、"))) : T("この PC には効くモデルがありません");
399
  }
400
  }
 
 
401
  function strictState() { // the strict option only under the memory-saving mode
402
  const on = $("f-mem_saver").checked;
403
  $("f-mem_saver_strict").disabled = !on;
@@ -448,7 +460,9 @@
448
  }
449
  async function saveSettings() {
450
  const body = {port: $("f-port").value, context: $("f-context").value, api_key: $("f-api_key").value.trim(), device: $("f-device").value,
451
- big_blocks: $("f-big_blocks").value, npuw: $("f-npuw").value, expert_gb: $("f-expert_gb").value, lang: $("f-lang").value};
 
 
452
  for (const k of ["pld", "autostart_server", "login_start", "lan", "update_check", "npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default"]) body[k] = $("f-" + k).checked;
453
  if (!body.mem_saver) body.mem_saver_strict = false;
454
  const oldLang = S.config.lang || "auto";
 
217
  <span class="desc">会話で使える最大の長さ。KV キャッシュは実際に使った分だけメモリを使います(1K トークンあたり 8〜80 MB)。変えても再コンパイルはありません。</span></div>
218
  <label class="swrow"><span class="switch"><input type="checkbox" id="f-lan"><i></i></span><span>LAN からの接続を許可する<span class="desc">他の PC から API を使えるようにします。API キーの設定をおすすめします。</span></span></label>
219
  <label class="swrow"><span class="switch"><input type="checkbox" id="f-think_default"><i></i></span><span>API で思考の指定がないリクエストも考えてから答える<span class="desc">enable_thinking を送らないエージェントのアプリ向け。回答は遅くなります。</span></span></label>
220
+ <div class="opt"><span class="k">API の既定の生成設定</span><select id="f-api_sampling" style="width:260px"><option value="greedy">貪欲(温度 0。毎回同じ答え)</option><option value="recommended">モデルの推奨値(チャット画面と同じ)</option><option value="custom">自分で指定</option></select>
221
+ <span class="desc">温度などを送らないアプリ(OpenCode など)のリクエストに使います。アプリが送った値はそのまま使います。</span></div>
222
+ <div class="opt" id="row-api_custom"><span class="k"></span>
223
+ <label class="small">温度 <input type="number" id="f-api_temperature" min="0" max="2" step="0.1" style="width:64px"></label>
224
+ <label class="small">Top P <input type="number" id="f-api_top_p" min="0.05" max="1" step="0.05" style="width:64px"></label>
225
+ <label class="small">Top K <input type="number" id="f-api_top_k" min="0" max="1000" step="1" style="width:64px"></label>
226
+ <label class="small">出現ペナルティ <input type="number" id="f-api_presence" min="0" max="2" step="0.1" style="width:64px"></label></div>
227
  </div>
228
 
229
  <div class="card set sub" data-sub="speed">
 
398
  $("f-device").value = c.device; $("f-big_blocks").value = c.big_blocks || "auto"; $("f-npuw").value = c.npuw || "auto"; $("f-expert_gb").value = String(c.expert_gb || "auto"); $("f-lang").value = c.lang || "auto";
399
  for (const k of ["pld", "autostart_server", "login_start", "lan", "update_check"]) $("f-" + k).checked = c[k] !== false && !!(c[k] ?? true);
400
  for (const k of ["npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default"]) $("f-" + k).checked = !!c[k];
401
+ $("f-api_sampling").value = c.api_sampling || "greedy";
402
+ for (const k of ["api_temperature", "api_top_p", "api_top_k", "api_presence"]) $("f-" + k).value = c[k] ?? "";
403
+ apiCustomState();
404
  strictState();
405
  const sm = s.saver_models || [];
406
  $("row-mem_saver").style.display = sm.length || c.mem_saver ? "" : "none";
 
408
  $("saver-models").textContent = sm.length ? T("この PC で効くモデル:{0}", sm.join(T("、"))) : T("この PC には効くモデルがありません");
409
  }
410
  }
411
+ function apiCustomState() { $("row-api_custom").style.display = $("f-api_sampling").value === "custom" ? "" : "none"; }
412
+ document.addEventListener("change", e => { if (e.target && e.target.id === "f-api_sampling") apiCustomState(); });
413
  function strictState() { // the strict option only under the memory-saving mode
414
  const on = $("f-mem_saver").checked;
415
  $("f-mem_saver_strict").disabled = !on;
 
460
  }
461
  async function saveSettings() {
462
  const body = {port: $("f-port").value, context: $("f-context").value, api_key: $("f-api_key").value.trim(), device: $("f-device").value,
463
+ big_blocks: $("f-big_blocks").value, npuw: $("f-npuw").value, expert_gb: $("f-expert_gb").value, lang: $("f-lang").value,
464
+ api_sampling: $("f-api_sampling").value, api_temperature: $("f-api_temperature").value, api_top_p: $("f-api_top_p").value,
465
+ api_top_k: $("f-api_top_k").value, api_presence: $("f-api_presence").value};
466
  for (const k of ["pld", "autostart_server", "login_start", "lan", "update_check", "npuw_moe", "npuw_async", "cache_size", "mem_saver", "mem_saver_strict", "think_default"]) body[k] = $("f-" + k).checked;
467
  if (!body.mem_saver) body.mem_saver_strict = false;
468
  const oldLang = S.config.lang || "auto";
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
 
5
  [project]
6
  name = "onw"
7
- version = "1.2.1"
8
  description = "Ore no NPU ga konna ni ugoku wake nai - LLMs entirely on the Intel NPU"
9
  readme = "README.md"
10
  license = {text = "Apache-2.0"}
 
4
 
5
  [project]
6
  name = "onw"
7
+ version = "1.2.2"
8
  description = "Ore no NPU ga konna ni ugoku wake nai - LLMs entirely on the Intel NPU"
9
  readme = "README.md"
10
  license = {text = "Apache-2.0"}