Swap LLM fleet to Ornith CRACK (small) and kingjones ROCmFP4 STRIX MTP (large); apply Qwen-Sharp chat template

- frank-default-small (Dankserver): Ornith-1.5-35B-A3B-CRACK Q8_0, bundled
  native draft-mtp (n_max 4), MoE expert FFN tensors pinned to CPU via an
  explicit --override-tensor \.ffn_(up|down|gate|gate_up)_(ch|)exps=CPU
  (replaces --n-cpu-moe), Qwen-Sharp chat template.
- frank-default-large (Dankclaw): kingjones Qwen3.8-27B-Uncensored-Q6_0
  ROCmFP4 AGENT dense GGUF, standalone mtp-Qwen3.8-27B-Q4_0 draft head
  (--model-draft, draft-mtp n_max 4), Qwen-Sharp chat template. Drop the
  retired PromptForge .pfs sidecar lanes.
- Track scripts/chat-template.jinja (v22.4.0 Sharp) and apply via
  --chat-template-file on both services.
- Docs: new kingjones-strix-mtp and dealign-ornith-crack runbook pages;
  update index, deployment, and both system specs; models.json names.

Smoke-tested both models on the real hardware on scratch ports before
commit: model loads, MTP draft attaches, Sharp template applies, chat
completion returns.
This commit is contained in:
Frank
2026-08-24 21:35:11 -07:00
parent 189934af15
commit 4462aa992c
14 changed files with 827 additions and 156 deletions
+453
View File
@@ -0,0 +1,453 @@
{%- set template_version = "qwen3.8-froggeric-v22.4.0" %}
{%- set _tool_format = tool_call_format if tool_call_format is defined else 'xml' %}
{%- set image_count = namespace(value=0) %}
{%- set video_count = namespace(value=0) %}
{%- set add_vision_id = add_vision_id if add_vision_id is defined else false %}
{%- set enable_thinking = enable_thinking if enable_thinking is defined else true %}
{%- set auto_disable_thinking_with_tools = auto_disable_thinking_with_tools if auto_disable_thinking_with_tools is defined else false %}
{%- if preserve_reasoning is defined and preserve_reasoning is not none %}
{%- set _preserve_thinking = preserve_reasoning %}
{%- elif preserve_thinking is defined and preserve_thinking is not none %}
{%- set _preserve_thinking = preserve_thinking %}
{%- else %}
{%- set _preserve_thinking = true %}
{%- endif %}
{%- set max_tool_arg_chars = max_tool_arg_chars if max_tool_arg_chars is defined else 0 %}
{%- set max_tool_response_chars = max_tool_response_chars if max_tool_response_chars is defined else 0 %}
{%- set _default_reasoning_effort = 'medium' %}
{%- set _has_tools = (tools is defined and tools and tools is iterable and tools is not mapping) %}
{%- set _effort_raw = (reasoning_effort | string | lower) if reasoning_effort is defined and reasoning_effort is not none else _default_reasoning_effort %}
{%- set _initial_thinking = enable_thinking %}
{%- if _effort_raw in ('none', 'off') %}
{%- set _initial_thinking = false %}
{%- set _initial_effort = 'medium' %}
{%- elif _effort_raw in ('minimal', 'low') %}
{%- set _initial_effort = 'low' %}
{%- elif _effort_raw in ('high', 'xhigh', 'max', 'ultracode', 'extreme') %}
{%- set _initial_effort = 'xhigh' %}
{%- else %}
{%- set _initial_effort = 'medium' %}
{%- endif %}
{%- set ns_state = namespace(thinking=_initial_thinking, effort=_initial_effort) %}
{%- if auto_disable_thinking_with_tools and _has_tools %}
{%- set ns_state.thinking = false %}
{%- endif %}
{%- for msg in messages %}
{%- if msg.role == 'system' or msg.role == 'developer' or msg.role == 'user' %}
{%- if msg.content is string %}
{%- if '<|think_off|>' in msg.content %}
{%- set ns_state.thinking = false %}
{%- elif '<|think_on|>' in msg.content %}
{%- set ns_state.thinking = true %}
{%- elif '<|think_xhigh|>' in msg.content or '<|think_high|>' in msg.content or '<|think_ultracode|>' in msg.content or '<|think_extreme|>' in msg.content or '<|think_max|>' in msg.content %}
{%- set ns_state.thinking = true %}
{%- set ns_state.effort = 'xhigh' %}
{%- elif '<|think_low|>' in msg.content or '<|think_minimal|>' in msg.content %}
{%- set ns_state.thinking = true %}
{%- set ns_state.effort = 'low' %}
{%- elif '<|think_medium|>' in msg.content %}
{%- set ns_state.thinking = true %}
{%- set ns_state.effort = 'medium' %}
{%- endif %}
{%- elif msg.content is iterable and msg.content is not mapping %}
{%- for item in msg.content %}
{%- if item is string %}
{%- set _item_text = item %}
{%- elif item is mapping and 'text' in item and item.text is string %}
{%- set _item_text = item.text %}
{%- else %}
{%- set _item_text = '' %}
{%- endif %}
{%- if _item_text %}
{%- if '<|think_off|>' in _item_text %}
{%- set ns_state.thinking = false %}
{%- elif '<|think_on|>' in _item_text %}
{%- set ns_state.thinking = true %}
{%- elif '<|think_xhigh|>' in _item_text or '<|think_high|>' in _item_text or '<|think_ultracode|>' in _item_text or '<|think_extreme|>' in _item_text or '<|think_max|>' in _item_text %}
{%- set ns_state.thinking = true %}
{%- set ns_state.effort = 'xhigh' %}
{%- elif '<|think_low|>' in _item_text or '<|think_minimal|>' in _item_text %}
{%- set ns_state.thinking = true %}
{%- set ns_state.effort = 'low' %}
{%- elif '<|think_medium|>' in _item_text %}
{%- set ns_state.thinking = true %}
{%- set ns_state.effort = 'medium' %}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- set reasoning_instructions = '' %}
{%- if ns_state.thinking %}
{%- if ns_state.effort == 'xhigh' %}
{%- set reasoning_instructions = 'Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.' %}
{%- elif ns_state.effort == 'low' %}
{%- set reasoning_instructions = 'Reasoning effort is set to low. Keep your thinking brief and focused, moving directly to the conclusion without unnecessary elaboration.' %}
{%- endif %}
{%- endif %}
{%- macro render_content(content, do_vision_count, is_system_content=false) %}
{%- if content is string %}
{{- content }}
{%- elif content is iterable and content is not mapping %}
{%- for item in content %}
{%- if item is mapping %}
{%- if item.type == 'image' or 'image' in item or 'image_url' in item %}
{%- if is_system_content %}
{{- raise_exception('System message cannot contain images.') }}
{%- endif %}
{%- if do_vision_count %}
{%- set image_count.value = image_count.value + 1 %}
{%- endif %}
{%- if add_vision_id %}
{{- 'Picture ' ~ image_count.value ~ ': ' }}
{%- endif %}
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
{%- elif item.type == 'video' or 'video' in item %}
{%- if is_system_content %}
{{- raise_exception('System message cannot contain videos.') }}
{%- endif %}
{%- if do_vision_count %}
{%- set video_count.value = video_count.value + 1 %}
{%- endif %}
{%- if add_vision_id %}
{{- 'Video ' ~ video_count.value ~ ': ' }}
{%- endif %}
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
{%- elif 'text' in item %}
{{- item.text }}
{%- else %}
{{- raise_exception('Unexpected item type in content.') }}
{%- endif %}
{%- else %}
{{- item | string }}
{%- endif %}
{%- endfor %}
{%- elif content is none or content is undefined %}
{{- '' }}
{%- else %}
{{- raise_exception('Unexpected content type.') }}
{%- endif %}
{%- endmacro %}
{%- if not messages %}
{{- raise_exception('No messages provided.') }}
{%- endif %}
{%- set head = namespace(count=0, seen_non_system=false) %}
{%- for message in messages %}
{%- set _is_sys = (message.role == 'system' or message.role == 'developer') %}
{%- if _is_sys and not head.seen_non_system %}
{%- set head.count = head.count + 1 %}
{%- else %}
{%- set head.seen_non_system = true %}
{%- endif %}
{%- endfor %}
{%- set sys_state = namespace(content='') %}
{%- for message in messages[:head.count] %}
{%- set _part = render_content(message.content, false, true) | trim %}
{%- if '<|think_off|>' in _part %}{%- set _part = _part.split('<|think_off|>') | join('') | trim %}{%- endif %}
{%- if '<|think_on|>' in _part %}{%- set _part = _part.split('<|think_on|>') | join('') | trim %}{%- endif %}
{%- if '<|think_xhigh|>' in _part %}{%- set _part = _part.split('<|think_xhigh|>') | join('') | trim %}{%- endif %}
{%- if '<|think_high|>' in _part %}{%- set _part = _part.split('<|think_high|>') | join('') | trim %}{%- endif %}
{%- if '<|think_ultracode|>' in _part %}{%- set _part = _part.split('<|think_ultracode|>') | join('') | trim %}{%- endif %}
{%- if '<|think_extreme|>' in _part %}{%- set _part = _part.split('<|think_extreme|>') | join('') | trim %}{%- endif %}
{%- if '<|think_max|>' in _part %}{%- set _part = _part.split('<|think_max|>') | join('') | trim %}{%- endif %}
{%- if '<|think_medium|>' in _part %}{%- set _part = _part.split('<|think_medium|>') | join('') | trim %}{%- endif %}
{%- if '<|think_low|>' in _part %}{%- set _part = _part.split('<|think_low|>') | join('') | trim %}{%- endif %}
{%- if '<|think_minimal|>' in _part %}{%- set _part = _part.split('<|think_minimal|>') | join('') | trim %}{%- endif %}
{%- if _part %}
{%- if sys_state.content %}
{%- set sys_state.content = sys_state.content ~ '\n\n' ~ _part %}
{%- else %}
{%- set sys_state.content = _part %}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- set _sc = sys_state.content %}
{%- set _msgs = messages[head.count:] %}
{%- if ns_state.thinking %}
{%- set _terse_lead = 'Answer directly, after thinking. Lead with the answer, then only what it needs to be correct and usable.' %}
{%- else %}
{%- set _terse_lead = 'Answer directly and concisely. Give the answer with only what it needs to be correct and usable.' %}
{%- endif %}
{%- set _terse_core %}
Never: open with preamble or pleasantries; restate the question; add filler transitions; hedge with niceties; or repeat a point you've already made.
Always: keep essential steps, caveats, uncertainties, and specifics — never drop correctness or a needed warning for brevity. Keep the final answer lean. Use the least structure that conveys it (plain prose when short; lists or code only when they earn their place). If genuinely uncertain, say so and explain why — never omit uncertainty for the sake of brevity.
If a user request is genuinely ambiguous, ask a sharp question, don't guess.
{%- endset %}
{%- set _terse = _terse_lead ~ '\n' ~ (_terse_core | trim) %}
{#- `terse` chat-template kwarg: default true, so behaviour is unchanged unless a caller opts out.
Pass {"terse": false} via chat_template_kwargs to serve the model with only its own system
prompt -- useful for A/B-ing the terseness effect, or when a downstream prompt conflicts. #}
{%- set _terse_on = terse if terse is defined else true %}
{%- if _terse_on %}
{%- if not _sc %}
{%- set _sc = _terse | trim %}
{%- else %}
{%- set _sc = (_sc | trim) ~ '\n\n' ~ (_terse | trim) %}
{%- endif %}
{%- endif %}
{%- if _has_tools %}
{{- '<|im_start|>system\n' }}
{%- if reasoning_instructions %}
{{- reasoning_instructions + '\n\n' }}
{%- endif %}
{{- '# Tools\n\nYou have access to the following functions:\n\n<tools>' }}
{%- for tool in tools %}
{{- '\n' }}
{{- tool | tojson }}
{%- endfor %}
{{- '\n</tools>' }}
{%- if _tool_format == 'json' %}
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n' }}{%- if ns_state.thinking %}{{- '<think>\nBrief explanation of tool call\n</think>\n' }}{%- endif %}{{- '<tool_call>\n{"name": "example_function_name", "arguments": {"example_parameter_1": "value_1", "example_parameter_2": "This is the value for the second parameter"}}\n</tool_call>\n\n<IMPORTANT>\nReminder:\n' }}{%- if ns_state.thinking %}{{- '- You can use the <think></think> block to plan your next tool call OR to synthesize data and formulate your final response to the user.\n- ALL explanation and reasoning MUST be placed strictly inside the <think></think> block.\n' }}{%- endif %}{{- '- Function calls MUST follow the specified format: a single JSON object with "name" and "arguments" keys inside <tool_call></tool_call> XML tags.\n- If you choose to call a tool, you MUST output the <tool_call> block IMMEDIATELY' }}{%- if ns_state.thinking %}{{- ' after thinking' }}{%- endif %}{{- ', with NO conversational text before it.\n- The <tool_call> tag MUST be at the very beginning of a new line, with NO spaces or indentation before it.\n- To call multiple functions, output a separate, completely closed <tool_call></tool_call> block for EACH function. Do NOT nest <tool_call> blocks.\n- If you have all necessary data, provide your final answer directly to the user without any tool call.\n</IMPORTANT>' }}
{%- else %}
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n' }}{%- if ns_state.thinking %}{{- '<think>\nBrief explanation of tool call\n</think>\n' }}{%- endif %}{{- '<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n' }}{%- if ns_state.thinking %}{{- '- You can use the <think></think> block to plan your next tool call OR to synthesize data and formulate your final response to the user.\n- ALL explanation and reasoning MUST be placed strictly inside the <think></think> block.\n' }}{%- endif %}{{- '- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags.\n- If you choose to call a tool, you MUST output the <tool_call> block IMMEDIATELY' }}{%- if ns_state.thinking %}{{- ' after thinking' }}{%- endif %}{{- ', with NO conversational text before it.\n- The <tool_call> and <function> tags MUST be at the very beginning of a new line, with NO spaces or indentation before them.\n- To call multiple functions, output a separate, completely closed <tool_call></tool_call> block for EACH function. Do NOT nest <tool_call> blocks.\n- If you have all necessary data, provide your final answer directly to the user without any tool call.\n</IMPORTANT>' }}
{%- endif %}
{%- if _sc %}
{{- '\n\n' + _sc }}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- else %}
{%- if _sc %}
{{- '<|im_start|>system\n' + (reasoning_instructions + '\n\n' if reasoning_instructions else '') + _sc + '<|im_end|>\n' }}
{%- elif reasoning_instructions %}
{{- '<|im_start|>system\n' + reasoning_instructions + '<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- set _last_idx = _msgs | length - 1 %}
{%- set ns = namespace(multi_step_tool=true, last_query_index=_last_idx) %}
{%- for message in _msgs[::-1] %}
{%- set index = (_msgs | length - 1) - loop.index0 %}
{%- if ns.multi_step_tool and message.role == 'user' %}
{%- set _rc = render_content(message.content, false) | trim %}
{%- if not (_rc.startswith('<tool_response>') and _rc.endswith('</tool_response>')) %}
{%- set ns.multi_step_tool = false %}
{%- set ns.last_query_index = index %}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- if ns.multi_step_tool %}
{%- if _last_idx > 50 %}
{%- set ns.last_query_index = _last_idx %}
{%- else %}
{%- set ns.last_query_index = 0 %}
{%- endif %}
{%- endif %}
{%- set ns2 = namespace(prev_role='', consecutive_failures=0) %}
{%- for message in _msgs %}
{%- set is_system = (message.role == "system" or message.role == "developer") %}
{%- set content = render_content(message.content, true, is_system) | trim %}
{%- if is_system or message.role == 'user' %}
{%- if '<|think_off|>' in content %}{%- set content = content.split('<|think_off|>') | join('') | trim %}{%- endif %}
{%- if '<|think_on|>' in content %}{%- set content = content.split('<|think_on|>') | join('') | trim %}{%- endif %}
{%- if '<|think_xhigh|>' in content %}{%- set content = content.split('<|think_xhigh|>') | join('') | trim %}{%- endif %}
{%- if '<|think_high|>' in content %}{%- set content = content.split('<|think_high|>') | join('') | trim %}{%- endif %}
{%- if '<|think_ultracode|>' in content %}{%- set content = content.split('<|think_ultracode|>') | join('') | trim %}{%- endif %}
{%- if '<|think_extreme|>' in content %}{%- set content = content.split('<|think_extreme|>') | join('') | trim %}{%- endif %}
{%- if '<|think_max|>' in content %}{%- set content = content.split('<|think_max|>') | join('') | trim %}{%- endif %}
{%- if '<|think_medium|>' in content %}{%- set content = content.split('<|think_medium|>') | join('') | trim %}{%- endif %}
{%- if '<|think_low|>' in content %}{%- set content = content.split('<|think_low|>') | join('') | trim %}{%- endif %}
{%- if '<|think_minimal|>' in content %}{%- set content = content.split('<|think_minimal|>') | join('') | trim %}{%- endif %}
{%- endif %}
{%- if is_system %}
{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
{%- elif message.role == 'user' %}
{%- set ns2.consecutive_failures = 0 %}
{{- '<|im_start|>user\n' + content + '<|im_end|>\n' }}
{%- elif message.role == 'assistant' %}
{%- set reasoning_content = '' %}
{%- set _explicit_reasoning = '' %}
{%- if message.reasoning_content is defined and message.reasoning_content is not none %}
{%- if message.reasoning_content is string %}
{%- set _explicit_reasoning = message.reasoning_content %}
{%- else %}
{%- set _explicit_reasoning = message.reasoning_content | string %}
{%- endif %}
{%- elif message.thinking is defined and message.thinking is not none %}
{%- if message.thinking is string %}
{%- set _explicit_reasoning = message.thinking %}
{%- else %}
{%- set _explicit_reasoning = message.thinking | string %}
{%- endif %}
{%- elif message.reasoning is defined and message.reasoning is not none %}
{%- if message.reasoning is string %}
{%- set _explicit_reasoning = message.reasoning %}
{%- else %}
{%- set _explicit_reasoning = message.reasoning | string %}
{%- endif %}
{%- endif %}
{%- if _explicit_reasoning %}
{%- set _lead_end = '' %}
{%- if content.startswith('<think>') and '</think>' in content %}
{%- set _lead_end = '</think>' %}
{%- elif content.startswith('<thinking>') and '</thinking>' in content %}
{%- set _lead_end = '</thinking>' %}
{%- elif content.startswith('</think>') %}
{%- set _lead_end = '</think>' %}
{%- elif content.startswith('</thinking>') %}
{%- set _lead_end = '</thinking>' %}
{%- endif %}
{%- if _lead_end %}
{%- set content = content.split(_lead_end)[-1].lstrip('\n') %}
{%- endif %}
{%- set reasoning_content = _explicit_reasoning %}
{%- else %}
{%- set _think_end = '' %}
{%- if content.startswith('</think>') %}
{%- set _think_end = '</think>' %}
{%- elif content.startswith('</thinking>') %}
{%- set _think_end = '</thinking>' %}
{%- elif '\n</think>' in content %}
{%- set _think_end = '\n</think>' %}
{%- elif '\n</thinking>' in content %}
{%- set _think_end = '\n</thinking>' %}
{%- elif '\n</ think>' in content %}
{%- set _think_end = '\n</ think>' %}
{%- elif '\n</think >' in content %}
{%- set _think_end = '\n</think >' %}
{%- elif content.startswith('<think>') and '</think>' in content %}
{%- set _think_end = '</think>' %}
{%- elif content.startswith('<thinking>') and '</thinking>' in content %}
{%- set _think_end = '</thinking>' %}
{%- endif %}
{%- if _think_end %}
{%- if 'thinking' in _think_end %}
{%- set _think_start = '<thinking>' %}
{%- else %}
{%- set _think_start = '<think>' %}
{%- endif %}
{%- set reasoning_content = content.split(_think_end)[0].rstrip('\n') %}
{%- if _think_start in reasoning_content %}
{%- set reasoning_content = reasoning_content.split(_think_start)[-1].lstrip('\n') %}
{%- endif %}
{%- set content = content.split(_think_end)[-1].lstrip('\n') %}
{%- endif %}
{%- endif %}
{%- set reasoning_content = reasoning_content | trim %}
{%- if (_preserve_thinking or loop.index0 > ns.last_query_index) %}
{{- '<|im_start|>assistant\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
{%- else %}
{{- '<|im_start|>assistant\n' + content }}
{%- endif %}
{%- if message.tool_calls is defined and message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
{%- for tool_call in message.tool_calls %}
{%- if tool_call.function is defined and tool_call.function is not none %}
{%- set tc = tool_call.function %}
{%- else %}
{%- set tc = tool_call %}
{%- endif %}
{%- set tc_name = tc.name if (tc.name is defined and tc.name is not none) else '' %}
{%- if _tool_format == 'json' %}
{%- if loop.first %}
{%- if content | trim %}
{{- '\n\n' }}
{%- endif %}
{%- else %}
{{- '\n' }}
{%- endif %}
{%- set _args = '{}' %}
{%- if tc.arguments is defined and tc.arguments is not none %}
{%- if tc.arguments is mapping %}
{%- set _args = tc.arguments | tojson %}
{%- elif tc.arguments is string %}
{%- if tc.arguments %}
{%- set _args = tc.arguments %}
{%- endif %}
{%- else %}
{%- set _args = tc.arguments | tojson %}
{%- endif %}
{%- endif %}
{{- '<tool_call>\n{"name": ' }}{{- tc_name | tojson }}{{- ', "arguments": ' }}{{- _args }}{{- '}\n</tool_call>' }}
{%- else %}
{%- if loop.first %}
{%- if content | trim %}
{{- '\n\n<tool_call>\n<function=' + tc_name + '>\n' }}
{%- else %}
{{- '<tool_call>\n<function=' + tc_name + '>\n' }}
{%- endif %}
{%- else %}
{{- '\n<tool_call>\n<function=' + tc_name + '>\n' }}
{%- endif %}
{%- if tc.arguments is defined and tc.arguments is not none %}
{%- if tc.arguments is mapping %}
{%- for args_name, args_value in tc.arguments.items() %}
{{- '<parameter=' + args_name + '>\n' }}
{%- if args_value is string %}
{%- set _av = args_value %}
{%- else %}
{%- set _av = args_value | tojson %}
{%- endif %}
{%- if max_tool_arg_chars > 0 and _av | length > max_tool_arg_chars %}
{{- _av[:max_tool_arg_chars] + '\n[TRUNCATED - original length ' ~ (_av | length | string) ~ ' chars]' }}
{%- else %}
{{- _av }}
{%- endif %}
{{- '\n</parameter>\n' }}
{%- endfor %}
{%- else %}
{%- if tc.arguments is string %}
{%- set _raw_args = tc.arguments %}
{%- else %}
{%- set _raw_args = tc.arguments | tojson %}
{%- endif %}
{%- if _raw_args %}
{%- if max_tool_arg_chars > 0 and _raw_args | length > max_tool_arg_chars %}
{{- _raw_args[:max_tool_arg_chars] + '\n[TRUNCATED - original length ' ~ (_raw_args | length | string) ~ ' chars]' }}
{%- else %}
{{- _raw_args }}
{%- endif %}
{%- endif %}
{%- endif %}
{%- endif %}
{{- '</function>\n</tool_call>' }}
{%- endif %}
{%- endfor %}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- elif message.role == 'tool' %}
{%- set _content_lower = content | lower %}
{%- set _content_head = _content_lower[:120] %}
{%- set _is_code_or_grep = ('throw new ' in _content_lower or 'throw error' in _content_lower or 'console.error' in _content_lower or 'logger.error' in _content_lower or 'logging.error' in _content_lower or 'import ' in _content_head or 'def ' in _content_head or 'function ' in _content_head) %}
{%- set _exit_code_zero = ('exit code: 0' in _content_head or 'process exited with code 0' in _content_head) %}
{%- set _error_field_ok = ('"error": null' in _content_head or '"error":null' in _content_head or '"error": false' in _content_head or '"error":false' in _content_head or '"error": ""' in _content_head or '"error":""' in _content_head) %}
{%- set _strong_error = (('"error":' in _content_head and not _error_field_ok) or '"status": "error"' in _content_head or '"status":"error"' in _content_head or 'traceback (most recent call last):' in _content_head or 'command not found' in _content_head or 'invalid syntax' in _content_head or 'fatal:' in _content_head or (('exit code: ' in _content_head or 'process exited with code' in _content_head) and not _exit_code_zero) or _content_head.startswith('exception:') or _content_head.startswith('failed to ')) %}
{%- set _weak_error = ('error:' in _content_head or 'err!' in _content_head) %}
{%- set _weak_suppressed = ('$ ' in _content_head or 'took ' in _content_head or content | length >= 600) %}
{%- if not _is_code_or_grep and (_strong_error or (_weak_error and not _weak_suppressed)) %}
{%- set ns2.consecutive_failures = ns2.consecutive_failures + 1 %}
{%- else %}
{%- set ns2.consecutive_failures = 0 %}
{%- endif %}
{%- if ns2.prev_role != 'tool' %}
{{- '<|im_start|>user' }}
{%- endif %}
{%- if _tool_format != 'json' and max_tool_response_chars > 0 and content | length > max_tool_response_chars %}
{%- set content = content[:max_tool_response_chars] + '\n[TRUNCATED - original length ' ~ (content | length | string) ~ ' chars]' %}
{%- endif %}
{{- '\n<tool_response>\n' + content }}
{%- if ns2.consecutive_failures >= 2 %}
{{- '\n\n⚠️ SYSTEM WARNING: ' ~ ns2.consecutive_failures ~ ' consecutive tool errors detected. Your previous approach is incorrect. You MUST use a fundamentally different approach or corrected arguments.' }}
{%- elif ns2.consecutive_failures == 1 %}
{{- '\n\n⚠️ SYSTEM WARNING: The previous tool call returned an error. Diagnose the failure and retry with completely corrected arguments.' }}
{%- endif %}
{{- '\n</tool_response>' }}
{%- if loop.last %}
{{- '<|im_end|>\n' }}
{%- else %}
{%- set _next_role = _msgs[loop.index0 + 1].role %}
{%- if _next_role != 'tool' %}
{{- '<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- else %}
{{- '<|im_start|>user\n[' + message.role + ']: ' + content + '<|im_end|>\n' }}
{%- endif %}
{%- set ns2.prev_role = message.role %}
{%- endfor %}
{%- if add_generation_prompt %}
{{- '<|im_start|>assistant\n' }}
{%- if not ns_state.thinking %}
{{- '<think>\n\n</think>\n\n' }}
{%- else %}
{{- '<think>\n' }}
{%- endif %}
{%- endif %}
+33 -42
View File
@@ -3,40 +3,36 @@
#
# Runtime: the PromptForge llama-server built from ciru-ai/ROCmFPX tag
# qwen3.8-activefpx-promptforge-v2.3 plus the v3 output-K8 runtime patch
# serving jcbtc/Qwen3.8-27B-CIRU-ActiveFPX-PromptForge (v3). v3 tensor
# allocation (FFN-equalized, late-Q6 writers, full-Q8 output) with the
# full-W8 PromptForge companions, the Output-K8 draft proxy, and the BF16
# vision projector. Alias frank-default-large, port 7770 (moved from 52625
# on 2026-08-22; the primary inference server sits on the standard
# llama.cpp port).
#
# This runtime replaced the retired vLLM service that previously occupied
# this slot. See docs/ciru-activefpx-promptforge.md.
# serving kingjones777/Qwen3.8-27B-Uncensored-ROCmFP4 (dense, Q6 AGENT
# GGUF) with the standalone MTP Q4_0 draft head. Alias frank-default-large,
# port 7770.
#
# This model replaced jcbtc/Qwen3.8-27B-CIRU-ActiveFPX-PromptForge (v3) on
# 2026-08-24. The new model is a plain ROCmFP4 GGUF (no PromptForge .pfs
# sidecars, no Output-K8 proxy); the requested file is Q6_.
# WARNING: do not name any variable TEMP, HOST, or GPU_MAX_HEAP_SIZE. The ROCm
# HIP runtime reads those bare env vars (see the 2026-08-13 TEMP incident).
# llama.cpp reads --host/--port/--temp as CLI flags, never as env vars.
# CIRU llama-server binary (ROCmFPX v2.3 + v3 output-K8 patch build, see
# docs/ciru runtime page). Built against the private TheRock ROCm 7.15 SDK
# kept in the retired ling3-ciru venv. ROCm 7.15 is required for the
# advertised Strix Halo performance; the system ROCm 7.2.4 selects
# slower/fallback kernels.
# CIRU llama-server binary (ROCmFPX v2.3 + v3 output-K8 patch build, built
# against the private TheRock ROCm 7.15 SDK kept in the retired ling3-ciru
# venv). ROCm 7.15 is required for the advertised Strix Halo performance.
LLAMA_BIN=/home/dank/ciru-qwen38/ROCmFPX/build-promptforge/bin/llama-server
# Model files (v3). The five-file payload (GGUF + FFN/GDN .pfs + Output-K8
# proxy + BF16 vision projector) is the full v3 configuration. The v3 files
# live in a separate directory so the v2 artifacts stay for rollback.
MODEL_DIR=/home/dank/models/CIRU-Qwen3.8-27B-ActiveFPX-v3
MODEL_FILE=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-v3-Q8-FFNEQ-LateQ6.gguf
FFN_PFS=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-FFN.pfs
GDN_PFS=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-GDN.pfs
OUTPUT_K8_PFS=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-Output-K8.pfs
MMPROJ_FILE=Qwen3.8-27B-mmproj-BF16.mmproj
# Model files. Plain dense Qwen3.8-27B ROCmFP4 Q6 AGENT GGUF plus the standalone
# MTP draft head and the vision projector. Kingjones 2026-08-24.
MODEL_DIR=/home/dank/models/kingjones-Qwen3.8-27B-STRIX-MTP
MODEL_FILE=Qwen3.8-27B-Uncensored-Q6_0_ROCMFPX_AGENT.gguf
MTP_DRAFT_FILE=mtp-Qwen3.8-27B-Q4_0.gguf
MMPROJ_FILE=mmproj-Qwen3.8-27B-Q8_0.gguf
# ROCm 7.15 SDK paths (inside the retired ling3-ciru venv). The serve script
# exports ROCM_PATH and LD_LIBRARY_PATH from these so the 7.15 libraries
# precede any system ROCm 7.2.4.
# Qwen-Sharp chat template (tracked in the repo, staged on the host).
CHAT_TEMPLATE_FILE=/home/dank/serverAppConfigs/scripts/chat-template.jinja
# enable_thinking on by default in the Sharp template; medium effort default.
CHAT_TEMPLATE_KWARGS='{"enable_thinking":true}'
# ROCm 7.15 SDK paths (inside the retired ling3-ciru venv).
ROCM_DEVEL=/home/dank/ling3-ciru/Ling-3.0-Flash-CIRU-int4-Strix-native/.venv/lib/python3.12/site-packages/_rocm_sdk_devel
ROCM_LIB=/home/dank/ling3-ciru/Ling-3.0-Flash-CIRU-int4-Strix-native/.venv/lib/python3.12/site-packages/_rocm_sdk_libraries
ROCM_CORE=/home/dank/ling3-ciru/Ling-3.0-Flash-CIRU-int4-Strix-native/.venv/lib/python3.12/site-packages/_rocm_sdk_core
@@ -45,13 +41,9 @@ ALIAS=frank-default-large
BIND_ADDR=0.0.0.0
LISTEN_PORT=7770
# Serving: full native 262144 context (n_ctx_train), 1 slot (user request
# 2026-08-19: parallel slots degraded performance). -c is the TOTAL
# context; with PARALLEL=1 it equals the per-slot context. Concurrent
# requests queue on the single slot; the server never preempts a running
# generation. f16 KV, context checkpoints, prompt cache, native MTP
# depth-4. The Strix Halo is unified memory; rocm-smi reports the 64 GiB
# budget. KV at 262144 x 64 KiB/cell is ~16 GiB.
# Serving: full native 262144 context, 1 slot. f16 KV, context checkpoints,
# prompt cache, draft-mtp with standalone Q4_0 head, n_max 4 (the knee for
# the dense model). Unified memory; rocm-smi reports the 64 GiB budget.
CTX_SIZE=262144
PARALLEL=1
BATCH_SIZE=4096
@@ -61,22 +53,21 @@ CACHE_TYPE_V=f16
CTX_CHECKPOINTS=16
CACHE_RAM=8192
# Native MTP draft (depth-4, p_min 0, backend sampling). The qualified v3
# profile drives the draft via the Output-K8 proxy; the CPU argmax fast
# path and IU4/ngram lanes are disabled in the serve script.
# Dense-model MTP draft (standalone Q4_0 head, n_max 4). No PromptForge .pfs
# sidecars are loaded; the LLAMA_CPU_ARGMAX fast path stays off.
SPEC_DRAFT_N_MAX=4
SPEC_DRAFT_N_MIN=0
SPEC_DRAFT_P_MIN=0.0
SPEC_DRAFT_P_SPLIT=0.10
# Sampling (model card defaults).
TEMPERATURE=0.7
# Sampling (kingjones model card / Sharp default).
TEMPERATURE=1.0
TOP_K=20
TOP_P=0.95
# Thinking enabled (user request, 2026-08-18). deepseek format moves the
# <think> reasoning into message.reasoning_content (OpenAI-compatible);
# none would leave the thought tags unparsed in content.
# Thinking on (Qwen-Sharp template drives thinking on; reasoning format
# projects the tar into reasoning_content).
REASONING=on
REASONING_FORMAT=deepseek
VERBOSITY=4
VERBOSITY=4
+1 -1
View File
@@ -1,5 +1,5 @@
[Unit]
Description=CIRU llama.cpp Qwen3.8-27B ActiveFPX PromptForge MTP (dankclaw, frank-default-large)
Description=CIRU llama.cpp Qwen3.8-27B ROCmFP4 STRIX MTP + Sharp template (dankclaw, frank-default-large)
After=network-online.target
Wants=network-online.target
+35 -24
View File
@@ -1,38 +1,49 @@
# Tunables for frank-default-small (HauhauCS Qwen3.6-35B-A3B Q6_K_P,
# ngram-mod spec, Dankserver, RTX 3090).
# Tunables for frank-default-small (Ornith-1.5-35B-A3B CRACK Q8_0, MTP MoE,
# Dankserver, RTX 3090).
#
# Runtime: upstream llama.cpp llama-server (b10121). RTX 3090 24 GB;
# Ryzen 7 7700X; 62 GB RAM. MoE experts offloaded to CPU (--n-cpu-moe 36).
# Restored 2026-08-18 (was the pre-2026-08-17-redo frank-default-small).
# Runtime: upstream llama.cpp llama-server (b10121). RTX 3090 24 GB; Ryzen
# 7 7700X; 62 GB RAM. MoE expert weights pinned to CPU with an explicit
# --override-tensor regex (not --n-cpu-moe). Replaced the HauhauCS
# Qwen3.6-35B-A3B model on 2026-08-24.
#
# WARNING: do not name any variable TEMP, HOST, or GPU_MAX_HEAP_SIZE. The
# ROCm HIP runtime reads those bare env vars (see the 2026-08-13 TEMP
# incident). This box is NVIDIA/CUDA, but the rule keeps the env portable.
# Model files (already on disk; Q6_K_P GGUF + f16 mmproj vision encoder).
MODEL_DIR=/home/dank/models
MODEL_FILE=Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf
MMPROJ_FILE=mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf
# Model files (Q8_0 CRACK GGUF + f16 mmproj vision encoder + MTP head bundled
# in the GGUF). Dealign2026-08-24.
MODEL_DIR=/home/dank/models/Ornith-1.5-35B-A3B-CRACK
MODEL_FILE=Ornith-1.5-35B-A3B-CRACK-Q8_0.gguf
MMPROJ_FILE=mmproj-Ornith-1.5-35B-A3B-f16.gguf
# Upstream llama.cpp binary (b10121).
LLAMA_SERVER=/home/dank/llama.cpp/build/bin/llama-server
# Qwen-Sharp chat template (tracked in the repo, staged on the host).
CHAT_TEMPLATE_FILE=/home/dank/serverAppConfigs/scripts/chat-template.jinja
ALIAS=frank-default-small
BIND_ADDR=0.0.0.0
LISTEN_PORT=7770
# 3 parallel slots x 262144 ctx each = ~786432 total context pool (max per-slot).
# Dual-f16 KV at 786432 cells leaves ~15 GiB free on the 3090.
# 3 parallel slots x 262144 total context pool (max per-slot), unchanged
# from the prior Qwen3.6 service. Lower PARALLEL if VRAM is tight with the
# larger Q8_0 payload.
CTX_SIZE=786432
PARALLEL=3
# Spec: ngram-mod (n_min=48, n_max=64, n_match=24).
SPEC_TYPE=ngram-mod
NGRAM_MOD_N_MIN=48
NGRAM_MOD_N_MAX=64
NGRAM_MOD_N_MATCH=24
# Spec: MoE model with a bundled MTP head -> native draft-mtp, n_max 4
# (the knee). ngram under MTP hurts; MTP alone is the proven best for MoE.
SPEC_TYPE=draft-mtp
SPEC_DRAFT_N_MAX=4
SPEC_DRAFT_N_MIN=0
SPEC_DRAFT_P_MIN=0.0
# Expert offload: 38 of 40 layers keep MoE weights on CPU; 2 on GPU.
N_CPU_MOE=38
# Expert offload via explicit tensor override (replaces --n-cpu-moe). Only
# the MoE FFN exps tensors move to CPU; everything else up to --gpu-layers
# stays on the 24 GB card. The regex is the same one the built-in --cpu-moe
# expands. Buffer type "CPU" is the CPU buft name on this host.
OVERRIDE_EXPERT='\.ffn_(up|down|gate|gate_up)_(ch|)exps'
OVERRIDE_BUFT=CPU
N_GPU_LAYERS=all
# KV cache on GPU, both K and V at f16.
@@ -40,22 +51,22 @@ CACHE_TYPE_K=f16
CACHE_TYPE_V=f16
VERBOSITY=4
# Store SWA/context checkpoints in RAM to hold peak GPU memory down.
# Store context checkpoints in RAM to hold peak GPU memory down.
CTX_CHECKPOINTS=16
# Big prefill batches (ubatch 2048, batch 4096).
# Big prefill batches.
BATCH_SIZE=4096
UBATCH_SIZE=2048
# Eager RAM load (MoE experts on CPU; ~31 GB RAM).
# Eager RAM load (expert weights on CPU; ~31 GB RAM).
NO_MMAP=1
# Sampling (HauhauCS Qwen3.6-35B-A3B model card, thinking-general).
# Sampling (Dealign Ornith model card, thinking-general).
TEMPERATURE=1.0
TOP_K=20
TOP_P=0.95
MIN_P=0.0
# Compute threads lowered to 7 so other processes keep a core.
# Compute threads lowered to 8 so other processes keep a core.
THREADS=7
THREADS_HTTP=8
THREADS_HTTP=8
+1 -1
View File
@@ -1,5 +1,5 @@
[Unit]
Description=llama.cpp Qwen3.6-35B-A3B Q6_K_P + ngram-mod (dankserver, frank-default-small)
Description=llama.cpp Ornith-1.5-35B-A3B CRACK Q8_0 MTP + Sharp template (dankserver, frank-default-small)
After=network-online.target
Wants=network-online.target
+34 -32
View File
@@ -1,19 +1,22 @@
#!/usr/bin/env bash
# Serve jcbtc/Qwen3.8-27B-CIRU-ActiveFPX-PromptForge (v3) as
# Serve kingjones777/Qwen3.8-27B-Uncensored-ROCmFP4-STRIX-MTP (dense) as
# frank-default-large via the CIRU ROCmFPX llama.cpp runtime on Dankclaw
# (Strix Halo gfx1151).
#
# Runtime: the PromptForge llama-server built from ciru-ai/ROCmFPX tag
# qwen3.8-activefpx-promptforge-v2.3 (commit 103af8b41) plus the v3 output-K8
# runtime patch (runtime/qwen38-v3-output-k8-runtime.patch, commit af0eac6 on
# Dankclaw) against the private TheRock ROCm 7.15 SDK (kept in the retired
# ling3-ciru venv). The model is the v3 GGUF with the full-W8 PromptForge
# companion views (FFN/GDN .pfs), the Output-K8 draft proxy, and the BF16
# vision projector. Native depth-4 MTP speculative decoding.
# runtime patch (commit af0eac6), against the private TheRock ROCm 7.15 SDK
# (kept in the retired ling3-ciru venv). The model is the plain ROCmFP4
# Q6 AGENT GGUF (no PromptForge .pfs sidecars) with the standalone MTP draft
# head mtp-Qwen3.8-27B-Q4_0.gguf. The Qwen-Sharp chat template is applied at
# runtime.
#
# Spec: dense model -> native MTP draft at n_max 4 (the knee, per the model
# card). The draft head is a separate Q4_0 GGUF loaded via --model-draft.
#
# The model card requires ROCm 7.15 (not the system ROCm 7.2.4) for the
# advertised Strix Halo performance. LD_LIBRARY_PATH therefore points at the
# 7.15 SDK libraries first. An older system ROCm must not precede them.
# 7.15 SDK libraries first.
#
# Tunables come from frank-default-large.env in this directory unless
# FRANK_LARGE_ENV_FILE overrides that path.
@@ -30,20 +33,19 @@ fi
# --- defaults (used when the env file does not set them) -------------------
: "${LLAMA_BIN:=/home/dank/ciru-qwen38/ROCmFPX/build-promptforge/bin/llama-server}"
: "${MODEL_DIR:=/home/dank/models/CIRU-Qwen3.8-27B-ActiveFPX-v3}"
: "${MODEL_FILE:=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-v3-Q8-FFNEQ-LateQ6.gguf}"
: "${FFN_PFS:=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-FFN.pfs}"
: "${GDN_PFS:=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-GDN.pfs}"
: "${OUTPUT_K8_PFS:=Qwen3.8-27B-CIRU-ActiveFPX-PromptForge-Output-K8.pfs}"
: "${MMPROJ_FILE:=Qwen3.8-27B-mmproj-BF16.mmproj}"
: "${MODEL_DIR:=/home/dank/models/kingjones-Qwen3.8-27B-STRIX-MTP}"
: "${MODEL_FILE:=Qwen3.8-27B-Uncensored-Q6_0_ROCMFPX_AGENT.gguf}"
: "${MTP_DRAFT_FILE:=mtp-Qwen3.8-27B-Q4_0.gguf}"
: "${MMPROJ_FILE:=mmproj-Qwen3.8-27B-Q8_0.gguf}"
: "${CHAT_TEMPLATE_FILE:=$SCRIPT_DIR/chat-template.jinja}"
: "${ROCM_DEVEL:=/home/dank/ling3-ciru/Ling-3.0-Flash-CIRU-int4-Strix-native/.venv/lib/python3.12/site-packages/_rocm_sdk_devel}"
: "${ROCM_LIB:=/home/dank/ling3-ciru/Ling-3.0-Flash-CIRU-int4-Strix-native/.venv/lib/python3.12/site-packages/_rocm_sdk_libraries}"
: "${ROCM_CORE:=/home/dank/ling3-ciru/Ling-3.0-Flash-CIRU-int4-Strix-native/.venv/lib/python3.12/site-packages/_rocm_sdk_core}"
: "${ALIAS:=frank-default-large}"
: "${BIND_ADDR:=0.0.0.0}"
: "${LISTEN_PORT:=7770}"
: "${CTX_SIZE:=524288}"
: "${PARALLEL:=2}"
: "${CTX_SIZE:=262144}"
: "${PARALLEL:=1}"
: "${BATCH_SIZE:=4096}"
: "${UBATCH_SIZE:=4096}"
: "${CACHE_TYPE_K:=f16}"
@@ -54,30 +56,27 @@ fi
: "${SPEC_DRAFT_N_MIN:=0}"
: "${SPEC_DRAFT_P_MIN:=0.0}"
: "${SPEC_DRAFT_P_SPLIT:=0.10}"
: "${TEMPERATURE:=0.7}"
: "${TEMPERATURE:=1.0}"
: "${TOP_K:=20}"
: "${TOP_P:=0.95}"
: "${REASONING:=on}"
: "${REASONING_FORMAT:=deepseek}"
: "${CHAT_TEMPLATE_KWARGS:={}}"
: "${VERBOSITY:=4}"
MODEL="$MODEL_DIR/$MODEL_FILE"
export PROMPTFORGE_SIDECAR="$MODEL_DIR/$FFN_PFS"
export PROMPTFORGE_GDN_SIDECAR="$MODEL_DIR/$GDN_PFS"
# v3 qualified runtime: Output-K8 draft proxy + small-row W8 prompt route.
export PROMPTFORGE_MODE="m2048_fused_tail1476"
export GGML_CUDA_GRAPH_OPT=0
export PROMPTFORGE_ENABLE_SMALLM_W8=1
export PROMPTFORGE_MTP_OUTPUT_K8=1
export PROMPTFORGE_MTP_OUTPUT_K8_VALIDATE=0
export PROMPTFORGE_MTP_OUTPUT_K8_PROXY="$MODEL_DIR/$OUTPUT_K8_PFS"
# The qualified v3 profile disables the CPU argmax draft fast path; the
# Output-K8 proxy + backend sampling replace it. Also drop any IU4/ngram
# companions from the non-IU4 development lane.
unset LLAMA_MTP_CPU_ARGMAX_FASTPATH LLAMA_MTP_BACKEND_GREEDY || true
MTP_DRAFT="$MODEL_DIR/$MTP_DRAFT_FILE"
# Plain dense model: no PromptForge .pfs sidecars, no Output-K8 proxy, no IU4
# lane. The v3 MTP is driven by the standalone Q4_0 draft head through the
# standard draft-mtp path. Unset any leftover PromptForge env lanes.
unset PROMPTFORGE_SIDECAR PROMPTFORGE_GDN_SIDECAR PROMPTFORGE_MTP_OUTPUT_K8_PROXY || true
unset PROMPTFORGE_MODE PROMPTFORGE_ENABLE_SMALLM_W8 || true
unset PROMPTFORGE_MTP_OUTPUT_K8 PROMPTFORGE_MTP_OUTPUT_K8_VALIDATE || true
unset PROMPTFORGE_IU4_SIDECAR PROMPTFORGE_GDN_IU4_SIDECAR || true
unset PROMPTFORGE_ENABLE_NGRAM_MOD PROMPTFORGE_ENABLE_NGRAM_M65_IU4 || true
for f in "$MODEL" "$PROMPTFORGE_SIDECAR" "$PROMPTFORGE_GDN_SIDECAR" "$PROMPTFORGE_MTP_OUTPUT_K8_PROXY" "$MODEL_DIR/$MMPROJ_FILE"; do
for f in "$MODEL" "$MTP_DRAFT" "$MODEL_DIR/$MMPROJ_FILE" "$CHAT_TEMPLATE_FILE"; do
if [[ ! -f "$f" ]]; then
echo "missing required file: $f" >&2
exit 1
@@ -95,6 +94,8 @@ export LD_LIBRARY_PATH="${ROCM_LIB}/lib:${ROCM_CORE}/lib:${ROCM_DEVEL}/lib${LD_L
exec "$LLAMA_BIN" \
--model "$MODEL" \
--mmproj "$MODEL_DIR/$MMPROJ_FILE" \
--chat-template-file "$CHAT_TEMPLATE_FILE" \
--chat-template-kwargs "$CHAT_TEMPLATE_KWARGS" \
--alias "$ALIAS" \
--host "$BIND_ADDR" \
--port "$LISTEN_PORT" \
@@ -113,6 +114,7 @@ exec "$LLAMA_BIN" \
--metrics \
--no-mmap \
--spec-type draft-mtp \
--model-draft "$MTP_DRAFT" \
--spec-draft-device ROCm0 \
--spec-draft-ngl 999 \
--spec-draft-type-k f16 \
@@ -121,10 +123,10 @@ exec "$LLAMA_BIN" \
--spec-draft-n-min "$SPEC_DRAFT_N_MIN" \
--spec-draft-p-min "$SPEC_DRAFT_P_MIN" \
--spec-draft-p-split "$SPEC_DRAFT_P_SPLIT" \
--spec-draft-backend-sampling \
--temp "$TEMPERATURE" \
--top-k "$TOP_K" \
--top-p "$TOP_P" \
--reasoning "$REASONING" \
--reasoning-format "$REASONING_FORMAT" \
--reasoning-budget -1 \
-lv "$VERBOSITY"
-lv "$VERBOSITY"
+36 -20
View File
@@ -1,13 +1,24 @@
#!/usr/bin/env bash
# Serve HauhauCS Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive:Q6_K_P with
# ngram-mod speculative decoding on Dankserver (RTX 3090, MoE experts on CPU).
# Serve dealignai/Ornith-1.5-35B-A3B-UNCENSORED (CRACK, Q8_0) as
# frank-default-small via upstream llama.cpp on Dankserver (RTX 3090, MoE
# experts on CPU).
#
# Runtime: upstream llama.cpp llama-server (b10121). The model is a 35B-total /
# 3B-active MoE vision-language model; --n-cpu-moe keeps most expert weights on
# CPU so the non-expert tensors + KV cache fit the 24 GB 3090.
# 3B-active MoE-dense hybrid with the MTP speculative head bundled in the GGUF.
#
# Tunables come from frank-default-small.env in this directory unless the
# FRANK_SMALL_ENV_FILE environment variable overrides that path.
# Expert offload: instead of the layer-counter --n-cpu-moe, the expert (FFN
# exps) tensors are pinned to CPU explicitly with --override-tensor. The
# pattern is the internal MoE exps regex (the same one --n-cpu-moe expands),
# so only the Mixture-of-Experts weight tensors move to CPU; every other
# tensor stays on the GPU up to --gpu-layers.
#
# Spec: MoE model with a bundled MTP head -> native draft-mtp (the head is
# auto-detected from the target model; b10121 creates the MTP draft context
# against the model file). ngram under MTP hurts (measured on the Qwen3.8
# family), so MTP alone is kept. n_max 4 is the knee.
#
# Tunables come from frank-default-small.env unless FRANK_SMALL_ENV_FILE
# overrides that path.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -21,22 +32,27 @@ fi
# --- defaults (used when the env file does not set them) -----------------
: "${MODEL_DIR:=/home/dank/models}"
: "${MODEL_FILE:=Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf}"
: "${MMPROJ_FILE:=mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf}"
: "${MODEL_FILE:=Ornith-1.5-35B-A3B-CRACK-Q8_0.gguf}"
: "${MMPROJ_FILE:=mmproj-Ornith-1.5-35B-A3B-f16.gguf}"
: "${LLAMA_SERVER:=/home/dank/llama.cpp/build/bin/llama-server}"
: "${CHAT_TEMPLATE_FILE:=$SCRIPT_DIR/chat-template.jinja}"
: "${ALIAS:=frank-default-small}"
: "${BIND_ADDR:=0.0.0.0}"
: "${LISTEN_PORT:=7770}"
: "${CTX_SIZE:=786432}"
: "${PARALLEL:=3}"
: "${SPEC_TYPE:=ngram-mod}"
: "${NGRAM_MOD_N_MIN:=48}"
: "${NGRAM_MOD_N_MAX:=64}"
: "${NGRAM_MOD_N_MATCH:=24}"
: "${SPEC_TYPE:=draft-mtp}"
: "${SPEC_DRAFT_N_MAX:=4}"
: "${SPEC_DRAFT_N_MIN:=0}"
: "${SPEC_DRAFT_P_MIN:=0.0}"
: "${CACHE_TYPE_K:=f16}"
: "${CACHE_TYPE_V:=f16}"
: "${VERBOSITY:=4}"
: "${N_CPU_MOE:=38}"
# Expert offload: --override-tensor regex pinned to the CPU buft. Same pattern
# the internal --cpu-moe / --n-cpu-moe use; explicit here so we only move the
# MoE expert weights and leave every other tensor on the GPU.
: "${OVERRIDE_EXPERT:=\.ffn_(up|down|gate|gate_up)_(ch|)exps}"
: "${OVERRIDE_BUFT:=CPU}"
: "${N_GPU_LAYERS:=all}"
: "${BATCH_SIZE:=4096}"
: "${UBATCH_SIZE:=2048}"
@@ -52,7 +68,7 @@ fi
MODEL="$MODEL_DIR/$MODEL_FILE"
MMPROJ="$MODEL_DIR/$MMPROJ_FILE"
for f in "$MODEL" "$MMPROJ"; do
for f in "$MODEL" "$MMPROJ" "$CHAT_TEMPLATE_FILE"; do
if [[ ! -f "$f" ]]; then
echo "missing required file: $f" >&2
exit 1
@@ -66,17 +82,17 @@ fi
exec "$LLAMA_SERVER" \
--model "$MODEL" \
--mmproj "$MMPROJ" \
--jinja \
--chat-template-file "$CHAT_TEMPLATE_FILE" \
--spec-type "$SPEC_TYPE" \
--spec-ngram-mod-n-min "$NGRAM_MOD_N_MIN" \
--spec-ngram-mod-n-max "$NGRAM_MOD_N_MAX" \
--spec-ngram-mod-n-match "$NGRAM_MOD_N_MATCH" \
--spec-draft-n-max "$SPEC_DRAFT_N_MAX" \
--spec-draft-n-min "$SPEC_DRAFT_N_MIN" \
--spec-draft-p-min "$SPEC_DRAFT_P_MIN" \
--override-tensor "$OVERRIDE_EXPERT=$OVERRIDE_BUFT" \
--cache-type-k "$CACHE_TYPE_K" \
--cache-type-v "$CACHE_TYPE_V" \
--verbosity "$VERBOSITY" \
--ctx-size "$CTX_SIZE" \
--parallel "$PARALLEL" \
--n-cpu-moe "$N_CPU_MOE" \
--gpu-layers "$N_GPU_LAYERS" \
--batch-size "$BATCH_SIZE" \
--ubatch-size "$UBATCH_SIZE" \
@@ -90,4 +106,4 @@ exec "$LLAMA_SERVER" \
--threads-http "$THREADS_HTTP" \
--alias "$ALIAS" \
--host "$BIND_ADDR" \
--port "$LISTEN_PORT"
--port "$LISTEN_PORT"