Compare commits
3 commits
main
...
studio-ink
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f178fa6114 | ||
|
|
d42d2db750 | ||
|
|
0986589089 |
11 changed files with 128 additions and 17 deletions
|
|
@ -652,6 +652,10 @@ _TOOL_TEMPLATE_MARKERS = (
|
||||||
"{%- if tools -%}",
|
"{%- if tools -%}",
|
||||||
"{% if tools %}",
|
"{% if tools %}",
|
||||||
"{% if tools -%}",
|
"{% if tools -%}",
|
||||||
|
# Defensive templates guard with `tools is defined` before truth-testing
|
||||||
|
# (e.g. Inkling: `{%- if tools is defined and tools -%}`).
|
||||||
|
"{%- if tools is defined",
|
||||||
|
"{% if tools is defined",
|
||||||
'"role" == "tool"',
|
'"role" == "tool"',
|
||||||
"'role' == 'tool'",
|
"'role' == 'tool'",
|
||||||
'message.role == "tool"',
|
'message.role == "tool"',
|
||||||
|
|
@ -667,9 +671,10 @@ _TOOL_TEMPLATE_MARKERS = (
|
||||||
|
|
||||||
|
|
||||||
# Canonical reasoning_effort levels, weakest -> strongest. Used to read the
|
# Canonical reasoning_effort levels, weakest -> strongest. Used to read the
|
||||||
# discrete set a template branches on (e.g. GLM-5.2 uses 'high' | 'max') so we
|
# discrete set a template branches on (e.g. GLM-5.2 uses 'high' | 'max', Inkling
|
||||||
# only ever offer levels the template actually understands.
|
# uses the full 'none'..'max' ladder) so we only ever offer levels the template
|
||||||
_REASONING_EFFORT_SCALE = ("minimal", "low", "medium", "high", "max")
|
# actually understands.
|
||||||
|
_REASONING_EFFORT_SCALE = ("none", "minimal", "low", "medium", "high", "xhigh", "max")
|
||||||
|
|
||||||
|
|
||||||
def _extract_reasoning_effort_levels(chat_template: str) -> list:
|
def _extract_reasoning_effort_levels(chat_template: str) -> list:
|
||||||
|
|
@ -751,10 +756,22 @@ def detect_reasoning_flags(
|
||||||
logger.info(f"{prefix}model supports reasoning (enable_thinking)")
|
logger.info(f"{prefix}model supports reasoning (enable_thinking)")
|
||||||
elif "reasoning_effort" in tpl:
|
elif "reasoning_effort" in tpl:
|
||||||
# gpt-oss / Harmony use reasoning_effort
|
# gpt-oss / Harmony use reasoning_effort
|
||||||
# ("low" | "medium" | "high"), not a boolean.
|
# ("low" | "medium" | "high"), not a boolean. Inkling maps a wider
|
||||||
|
# named ladder ('none'..'max'); surface the levels the template
|
||||||
|
# actually branches on so the Think menu offers exactly those and
|
||||||
|
# nothing more ('none' renders as thinking off: effort 0). Guard:
|
||||||
|
# trust the scan only when it includes the core 'low' and 'high'
|
||||||
|
# literals, so a template that quotes e.g. 'none' for an unrelated
|
||||||
|
# comparison keeps the default low/medium/high set.
|
||||||
flags["supports_reasoning"] = True
|
flags["supports_reasoning"] = True
|
||||||
flags["reasoning_style"] = "reasoning_effort"
|
flags["reasoning_style"] = "reasoning_effort"
|
||||||
logger.info(f"{prefix}model supports reasoning (reasoning_effort)")
|
scanned = _extract_reasoning_effort_levels(tpl)
|
||||||
|
if "low" in scanned and "high" in scanned:
|
||||||
|
flags["reasoning_effort_levels"] = scanned
|
||||||
|
logger.info(
|
||||||
|
f"{prefix}model supports reasoning "
|
||||||
|
f"(reasoning_effort: {flags['reasoning_effort_levels'] or 'default levels'})"
|
||||||
|
)
|
||||||
elif "thinking" in tpl:
|
elif "thinking" in tpl:
|
||||||
# DeepSeek uses 'thinking', not 'enable_thinking'
|
# DeepSeek uses 'thinking', not 'enable_thinking'
|
||||||
normalized_id = (model_identifier or "").lower()
|
normalized_id = (model_identifier or "").lower()
|
||||||
|
|
@ -1524,6 +1541,25 @@ def _is_external_link(path: Path) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Inkling's template takes a numeric thinking-effort dial (0..0.99) and its
|
||||||
|
# float() coercion turns unrecognized named levels into 0, i.e. no thinking.
|
||||||
|
# Map OpenAI-style names to the values the model was trained on. Module-level
|
||||||
|
# so duck-typed engine stand-ins in tests do not need the attribute.
|
||||||
|
_INKLING_REASONING_EFFORT = {
|
||||||
|
"none": 0.0, "minimal": 0.2, "low": 0.2, "medium": 0.7,
|
||||||
|
"high": 0.9, "xhigh": 0.99, "max": 0.99,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _coerce_reasoning_effort(architecture, kwargs: dict) -> dict:
|
||||||
|
if architecture == "inkling":
|
||||||
|
effort = kwargs.get("reasoning_effort")
|
||||||
|
if isinstance(effort, str):
|
||||||
|
mapped = _INKLING_REASONING_EFFORT.get(effort.strip().lower())
|
||||||
|
if mapped is not None:
|
||||||
|
kwargs["reasoning_effort"] = mapped
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class LlamaCppBackend:
|
class LlamaCppBackend:
|
||||||
"""Manages a llama-server subprocess for GGUF model inference.
|
"""Manages a llama-server subprocess for GGUF model inference.
|
||||||
|
|
||||||
|
|
@ -1930,7 +1966,9 @@ class LlamaCppBackend:
|
||||||
# the template's default effort (max) in place.
|
# the template's default effort (max) in place.
|
||||||
return {"enable_thinking": enable_thinking}
|
return {"enable_thinking": enable_thinking}
|
||||||
if self._reasoning_style == "reasoning_effort":
|
if self._reasoning_style == "reasoning_effort":
|
||||||
return {"reasoning_effort": "high" if enable_thinking else "low"}
|
return _coerce_reasoning_effort(
|
||||||
|
getattr(self, "_architecture", None),
|
||||||
|
{"reasoning_effort": "high" if enable_thinking else "low"})
|
||||||
return {"enable_thinking": enable_thinking}
|
return {"enable_thinking": enable_thinking}
|
||||||
|
|
||||||
def _request_reasoning_kwargs(
|
def _request_reasoning_kwargs(
|
||||||
|
|
@ -1978,6 +2016,7 @@ class LlamaCppBackend:
|
||||||
kwargs["enable_thinking"] = enable_thinking
|
kwargs["enable_thinking"] = enable_thinking
|
||||||
if self._supports_preserve_thinking and preserve_thinking is not None:
|
if self._supports_preserve_thinking and preserve_thinking is not None:
|
||||||
kwargs["preserve_thinking"] = preserve_thinking
|
kwargs["preserve_thinking"] = preserve_thinking
|
||||||
|
_coerce_reasoning_effort(getattr(self, "_architecture", None), kwargs)
|
||||||
return kwargs or None
|
return kwargs or None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -3505,6 +3544,19 @@ class LlamaCppBackend:
|
||||||
_DSV4_CTX_COMPUTE_FLAT_BYTES = 2 * 1024**3 # ctx-independent indexer scratch
|
_DSV4_CTX_COMPUTE_FLAT_BYTES = 2 * 1024**3 # ctx-independent indexer scratch
|
||||||
_DSV4_CTX_COMPUTE_BYTES_PER_TOK = 72000 # per token at ub=512 (~72 GiB at 1M)
|
_DSV4_CTX_COMPUTE_BYTES_PER_TOK = 72000 # per token at ub=512 (~72 GiB at 1M)
|
||||||
|
|
||||||
|
# Inkling (inkling): with the reserve fix in the bundled llama.cpp (full-cache
|
||||||
|
# reserve context reports the whole cache as position-contiguous, so the
|
||||||
|
# worst-case graph is the banded flash path instead of the dense-bias
|
||||||
|
# fallback), the ctx-linear compute term is just the banded KQ-mask cont:
|
||||||
|
# measured on UD-IQ1_S (ub=512) 64K -> 1M total-VRAM slope of 50.6 KiB/tok,
|
||||||
|
# of which 45,056 B/tok is KV -> ~5.6 KiB/tok compute. 8192 adds ~1.5x
|
||||||
|
# headroom. (Pre-fix builds reserved the dense fallback at ~402 KiB/tok and
|
||||||
|
# could not load large contexts at all.)
|
||||||
|
_INKLING_CTX_COMPUTE_BYTES_PER_TOK = 8192 # per token at ub=512
|
||||||
|
# Dense relative-bias fallback rate (quantized KV cache disables the banded
|
||||||
|
# path): measured 402.5 GiB reserve at 1M ctx pre-fix, ~402 KiB per token.
|
||||||
|
_INKLING_CTX_COMPUTE_DENSE_BYTES_PER_TOK = 402470 # per token at ub=512
|
||||||
|
|
||||||
def _estimate_compute_buffer_bytes(
|
def _estimate_compute_buffer_bytes(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
|
@ -3561,6 +3613,17 @@ class LlamaCppBackend:
|
||||||
self._DSV4_CTX_COMPUTE_FLAT_BYTES
|
self._DSV4_CTX_COMPUTE_FLAT_BYTES
|
||||||
+ self._DSV4_CTX_COMPUTE_BYTES_PER_TOK * n_ctx * ub_scale
|
+ self._DSV4_CTX_COMPUTE_BYTES_PER_TOK * n_ctx * ub_scale
|
||||||
)
|
)
|
||||||
|
if getattr(self, "_architecture", None) == "inkling":
|
||||||
|
ub_scale = ub / self._DEFAULT_N_UBATCH
|
||||||
|
# The fused banded path requires an f32/f16/bf16 KV cache. A quantized
|
||||||
|
# cache forces the dense relative-bias fallback, whose compute buffer is
|
||||||
|
# [n_kv, ub, n_head] f32 (~402 KiB per context token at ub=512); size the
|
||||||
|
# fit for that so a q8_0 cache gets a small honest context instead of an
|
||||||
|
# unloadable one that crash-loops the server.
|
||||||
|
if cache_type_kv and _kv_bytes_per_elem(cache_type_kv) < 2.0:
|
||||||
|
return int(self._INKLING_CTX_COMPUTE_DENSE_BYTES_PER_TOK * n_ctx * ub_scale)
|
||||||
|
# Banded flash path (see constants): linear, ub-scaled.
|
||||||
|
return int(self._INKLING_CTX_COMPUTE_BYTES_PER_TOK * n_ctx * ub_scale)
|
||||||
if _kv_bytes_per_elem(cache_type_kv) < 2.0:
|
if _kv_bytes_per_elem(cache_type_kv) < 2.0:
|
||||||
# Quantized cache: the dequant scratch dominates and scales with n_embd.
|
# Quantized cache: the dequant scratch dominates and scales with n_embd.
|
||||||
# MLA (compressed KV) needs far less of it: measured 0.94 x n_embd on
|
# MLA (compressed KV) needs far less of it: measured 0.94 x n_embd on
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,9 @@ _HEAL_SIGNALS = (
|
||||||
"<|tool_call>",
|
"<|tool_call>",
|
||||||
"<function=",
|
"<function=",
|
||||||
"[TOOL_CALLS]",
|
"[TOOL_CALLS]",
|
||||||
|
# TML Inkling native call marker (leaks as text when the server-side
|
||||||
|
# parser misses a narration-then-call turn).
|
||||||
|
"<|content_invoke_tool_json|>",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ TOOL_XML_SIGNALS = (
|
||||||
# Kimi K2 / Moonshot.
|
# Kimi K2 / Moonshot.
|
||||||
"<|tool_calls_section_begin|>",
|
"<|tool_calls_section_begin|>",
|
||||||
"<|tool_call_begin|>",
|
"<|tool_call_begin|>",
|
||||||
|
# TML Inkling native call marker.
|
||||||
|
"<|content_invoke_tool_json|>",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,10 +124,12 @@ def apply_tool_strip_patterns(
|
||||||
|
|
||||||
|
|
||||||
# Pre-compiled patterns for tool-call XML parsing.
|
# Pre-compiled patterns for tool-call XML parsing.
|
||||||
_TC_JSON_START_RE = re.compile(r"<tool_call>\s*\{")
|
# <|content_invoke_tool_json|> is TML Inkling's native call marker; its JSON uses
|
||||||
|
# an ``args`` key and the block closes with <|end_message|>.
|
||||||
|
_TC_JSON_START_RE = re.compile(r"(?:<tool_call>|<\|content_invoke_tool_json\|>)\s*\{")
|
||||||
_TC_GEMMA_START_RE = re.compile(r"<\|tool_call>\s*call\s*:\s*([\w.\-]+)\s*\{")
|
_TC_GEMMA_START_RE = re.compile(r"<\|tool_call>\s*call\s*:\s*([\w.\-]+)\s*\{")
|
||||||
_TC_FUNC_START_RE = re.compile(r"<function=([\w-]+)>\s*")
|
_TC_FUNC_START_RE = re.compile(r"<function=([\w-]+)>\s*")
|
||||||
_TC_END_TAG_RE = re.compile(r"</tool_call>")
|
_TC_END_TAG_RE = re.compile(r"</tool_call>|<\|end_message\|>")
|
||||||
_TC_GEMMA_END_TAG_RE = re.compile(r"<tool_call\|>")
|
_TC_GEMMA_END_TAG_RE = re.compile(r"<tool_call\|>")
|
||||||
_TC_FUNC_CLOSE_RE = re.compile(r"\s*</function>\s*$")
|
_TC_FUNC_CLOSE_RE = re.compile(r"\s*</function>\s*$")
|
||||||
# Horizontal-whitespace trailing class keeps the wrapping newline; _trim_param_value trims it.
|
# Horizontal-whitespace trailing class keeps the wrapping newline; _trim_param_value trims it.
|
||||||
|
|
@ -686,12 +688,24 @@ def parse_tool_calls_from_text(
|
||||||
if kind == "json":
|
if kind == "json":
|
||||||
obj = json.loads(content[m.end() - 1 : brace_end + 1])
|
obj = json.loads(content[m.end() - 1 : brace_end + 1])
|
||||||
name = obj.get("name", "")
|
name = obj.get("name", "")
|
||||||
# Accept ``parameters`` alias for ``arguments`` (Llama-3.2 drift inside Hermes).
|
# Accept ``parameters`` alias for ``arguments`` (Llama-3.2 drift inside
|
||||||
|
# Hermes) and ``args`` (TML Inkling native calls).
|
||||||
arguments = obj.get("arguments")
|
arguments = obj.get("arguments")
|
||||||
if arguments is None:
|
if arguments is None:
|
||||||
arguments = obj.get("parameters", {})
|
arguments = obj.get("parameters")
|
||||||
|
if arguments is None:
|
||||||
|
arguments = obj.get("args", {})
|
||||||
if isinstance(arguments, dict):
|
if isinstance(arguments, dict):
|
||||||
arguments = json.dumps(arguments)
|
arguments = json.dumps(arguments)
|
||||||
|
# Inkling echoes the bare tool name (and a role opener) before the
|
||||||
|
# marker: <|message_model|>NAME<|content_invoke_tool_json|>{...}.
|
||||||
|
# Fold that echo into the markup span so promotion removes it too.
|
||||||
|
if name and content.startswith("<|content_invoke_tool_json|>", start):
|
||||||
|
pre = content[:start]
|
||||||
|
if pre.endswith(name):
|
||||||
|
start -= len(name)
|
||||||
|
if content[:start].endswith("<|message_model|>"):
|
||||||
|
start -= len("<|message_model|>")
|
||||||
else:
|
else:
|
||||||
name = m.group(1)
|
name = m.group(1)
|
||||||
arguments = json.dumps(_gemma_arguments_to_json(content[m.end() : brace_end]))
|
arguments = json.dumps(_gemma_arguments_to_json(content[m.end() : brace_end]))
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,10 @@ def spawn_worker(
|
||||||
env["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
|
env["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
|
||||||
env["HF_HUB_DISABLE_TELEMETRY"] = "1"
|
env["HF_HUB_DISABLE_TELEMETRY"] = "1"
|
||||||
env["HF_HUB_DISABLE_XET"] = "0" if use_xet else "1"
|
env["HF_HUB_DISABLE_XET"] = "0" if use_xet else "1"
|
||||||
|
# No token in Studio settings: fall back to the backend's own HF_TOKEN so
|
||||||
|
# private repos stay downloadable (needed while inkling repos are private).
|
||||||
|
if not hf_token:
|
||||||
|
hf_token = os.environ.get("HF_TOKEN") or None
|
||||||
env["HF_HUB_DISABLE_IMPLICIT_TOKEN"] = "0" if hf_token else "1"
|
env["HF_HUB_DISABLE_IMPLICIT_TOKEN"] = "0" if hf_token else "1"
|
||||||
# hf_transfer's parallel Range chunks can leave sparse partials even in
|
# hf_transfer's parallel Range chunks can leave sparse partials even in
|
||||||
# "http" mode; disable so the worker's writer is always sequential.
|
# "http" mode; disable so the worker's writer is always sequential.
|
||||||
|
|
|
||||||
|
|
@ -1422,6 +1422,7 @@ class TestHealerSignalAlignment:
|
||||||
"<|tool_call>",
|
"<|tool_call>",
|
||||||
"<function=",
|
"<function=",
|
||||||
"[TOOL_CALLS]",
|
"[TOOL_CALLS]",
|
||||||
|
"<|content_invoke_tool_json|>",
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_prose_with_bare_args_marker_streams_through(self):
|
def test_prose_with_bare_args_marker_streams_through(self):
|
||||||
|
|
|
||||||
|
|
@ -874,7 +874,7 @@ class TestHealerSignalAlignment:
|
||||||
|
|
||||||
def test_heal_signals_subset_of_promotable_formats(self):
|
def test_heal_signals_subset_of_promotable_formats(self):
|
||||||
from core.inference.passthrough_healing import _HEAL_SIGNALS
|
from core.inference.passthrough_healing import _HEAL_SIGNALS
|
||||||
assert set(_HEAL_SIGNALS) == {"<tool_call>", "<|tool_call>", "<function=", "[TOOL_CALLS]"}
|
assert set(_HEAL_SIGNALS) == {"<tool_call>", "<|tool_call>", "<function=", "[TOOL_CALLS]", "<|content_invoke_tool_json|>"}
|
||||||
|
|
||||||
def test_stream_healer_does_not_hold_llama_python_tag_text(self):
|
def test_stream_healer_does_not_hold_llama_python_tag_text(self):
|
||||||
from core.inference.passthrough_healing import StreamToolCallHealer
|
from core.inference.passthrough_healing import StreamToolCallHealer
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import {
|
||||||
useAui,
|
useAui,
|
||||||
useAuiState,
|
useAuiState,
|
||||||
} from "@assistant-ui/react";
|
} from "@assistant-ui/react";
|
||||||
import { File02Icon } from "@hugeicons/core-free-icons";
|
import { AudioWave01Icon, File02Icon } from "@hugeicons/core-free-icons";
|
||||||
import { HugeiconsIcon } from "@hugeicons/react";
|
import { HugeiconsIcon } from "@hugeicons/react";
|
||||||
import { PlusIcon, XIcon } from "lucide-react";
|
import { PlusIcon, XIcon } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
|
|
@ -120,9 +120,20 @@ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AUDIO_ATTACHMENT_RE = /\.(wav|mp3|m4a|ogg|oga|flac|webm|mp4|aac)$/i;
|
||||||
|
|
||||||
|
const isAudioAttachment = (name: string | undefined, contentType: string) =>
|
||||||
|
/^audio\//i.test(contentType) || AUDIO_ATTACHMENT_RE.test(name ?? "");
|
||||||
|
|
||||||
const AttachmentThumb: FC = () => {
|
const AttachmentThumb: FC = () => {
|
||||||
const src = useAttachmentSrc();
|
const src = useAttachmentSrc();
|
||||||
const name = useAuiState(({ attachment }) => attachment.name);
|
const name = useAuiState(({ attachment }) => attachment.name);
|
||||||
|
const contentType = useAuiState(
|
||||||
|
({ attachment }) =>
|
||||||
|
(attachment as { file?: File }).file?.type ??
|
||||||
|
(attachment as { contentType?: string }).contentType ??
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
if (src) {
|
if (src) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -137,7 +148,7 @@ const AttachmentThumb: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<HugeiconsIcon
|
<HugeiconsIcon
|
||||||
icon={File02Icon}
|
icon={isAudioAttachment(name, contentType) ? AudioWave01Icon : File02Icon}
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
className="size-6 text-muted-foreground"
|
className="size-6 text-muted-foreground"
|
||||||
/>
|
/>
|
||||||
|
|
@ -159,7 +170,12 @@ const AttachmentUI: FC = () => {
|
||||||
case "document":
|
case "document":
|
||||||
return "Document";
|
return "Document";
|
||||||
case "file":
|
case "file":
|
||||||
return "File";
|
return isAudioAttachment(
|
||||||
|
attachment.name,
|
||||||
|
(attachment as { file?: File }).file?.type ?? "",
|
||||||
|
)
|
||||||
|
? "Audio"
|
||||||
|
: "File";
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown attachment type: ${type as string}`);
|
throw new Error(`Unknown attachment type: ${type as string}`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2315,7 +2315,13 @@ const ReasoningToggle: FC<{ side?: "top" | "bottom" }> = ({
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
{effectiveReasoningEffortLevels
|
{effectiveReasoningEffortLevels
|
||||||
.filter((level) => level !== "none")
|
// 'none' is a real template level for models like Inkling
|
||||||
|
// (effort 0 = thinking off); show it as a pick unless the
|
||||||
|
// dedicated off item above already covers it.
|
||||||
|
.filter(
|
||||||
|
(level) =>
|
||||||
|
level !== "none" || !effectiveSupportsReasoningOff,
|
||||||
|
)
|
||||||
.map((level) => (
|
.map((level) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={level}
|
key={level}
|
||||||
|
|
|
||||||
|
|
@ -798,7 +798,8 @@ export function useChatModelRuntime() {
|
||||||
: (["low", "medium", "high"] as const);
|
: (["low", "medium", "high"] as const);
|
||||||
const existingReasoningEffort = useChatRuntimeStore.getState().reasoningEffort;
|
const existingReasoningEffort = useChatRuntimeStore.getState().reasoningEffort;
|
||||||
const clampedReasoningEffort =
|
const clampedReasoningEffort =
|
||||||
reasoningStyle === "enable_thinking_effort"
|
reasoningStyle === "enable_thinking_effort" ||
|
||||||
|
reasoningStyle === "reasoning_effort"
|
||||||
? clampReasoningEffortToLevels(
|
? clampReasoningEffortToLevels(
|
||||||
existingReasoningEffort,
|
existingReasoningEffort,
|
||||||
reasoningEffortLevels,
|
reasoningEffortLevels,
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,8 @@ export function applyActiveModelStatusToStore(
|
||||||
const currentSpecType = normalizeSpeculativeType(status.speculative_type);
|
const currentSpecType = normalizeSpeculativeType(status.speculative_type);
|
||||||
const prevState = useChatRuntimeStore.getState();
|
const prevState = useChatRuntimeStore.getState();
|
||||||
const clampedReasoningEffort =
|
const clampedReasoningEffort =
|
||||||
reasoningStyle === "enable_thinking_effort"
|
reasoningStyle === "enable_thinking_effort" ||
|
||||||
|
reasoningStyle === "reasoning_effort"
|
||||||
? clampReasoningEffortToLevels(
|
? clampReasoningEffortToLevels(
|
||||||
prevState.reasoningEffort,
|
prevState.reasoningEffort,
|
||||||
reasoningEffortLevels,
|
reasoningEffortLevels,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue