Merge current dev and refresh PR #6738 from the 7011 integration

This commit is contained in:
pewdiepie-archdaemon
2026-10-26 16:21:22 +00:00
450 changed files with 60479 additions and 11016 deletions
+73 -3
View File
@@ -98,7 +98,9 @@ def clean_repeated_assistant_content(text: object) -> str:
"",
value,
).strip()
value = re.sub(r"(?is)\s*</\s*think\s*>\s*$", "", value).strip()
# No leading `\s*`: .strip() removes that whitespace anyway, and scanning
# it from every offset of a long whitespace run was quadratic (ReDoS).
value = re.sub(r"(?is)</\s*think\s*>\s*$", "", value).strip()
return value
_CASUAL_OPENING_RE = re.compile(
@@ -549,6 +551,7 @@ async def auto_name_session(session_manager, sess):
try:
from src.llm_core import llm_call_async
from src.task_endpoint import resolve_task_endpoint
from src.utility_effort import effort_for_call
# Find first user message
first_msg = ""
@@ -568,6 +571,50 @@ async def auto_name_session(session_manager, sess):
endpoint_url = str(getattr(sess, "endpoint_url", "") or "")
model_name = str(getattr(sess, "model", "") or "")
# The spec ("Ajax") model names its own chats from the user's first
# prompt. This function only runs after the first response stream has
# settled (auto_name_session_after_stream), so titling does not
# contend with the visible answer. Thinking is disabled and the
# budget kept small so the model returns a bare title.
try:
from src.model_profiles import uses_odysseus_progressive_thinking
_is_spec_model = bool(model_name) and uses_odysseus_progressive_thinking(model_name)
except Exception:
_is_spec_model = False
if _is_spec_model and endpoint_url:
_headers = getattr(sess, "headers", None)
if isinstance(_headers, str):
try:
_headers = json.loads(_headers)
except Exception:
_headers = None
try:
title = await llm_call_async(
endpoint_url,
model_name,
[
{"role": "system", "content": "Generate a short title (3-6 words, no quotes) for a conversation that starts with this message. Reply with ONLY the title, nothing else. Do NOT include any thinking, reasoning, or explanation \u2014 just the title."},
{"role": "user", "content": first_msg},
],
temperature=0.3,
max_tokens=32,
headers=_headers or None,
timeout=20,
thinking_mode="off",
)
except Exception as e:
logger.warning("[auto-name] spec title call failed for %s: %s", sess.id, e)
title = ""
title = str(title or "").strip().strip('"\'').strip()
from src.text_helpers import strip_think
title = strip_think(title, prose=False, prompt_echo=False)
if not title or len(title) >= 80 or "\n" in title:
title = fallback_session_title(first_msg)
session_manager.update_session_name(sess.id, title)
logger.info(f"Auto-named spec session {sess.id}: {title}")
return
if (
"ttft" in model_name.lower()
or re.search(r":18\d{3}\b", endpoint_url)
@@ -601,6 +648,7 @@ async def auto_name_session(session_manager, sess):
max_tokens=64,
headers=t_headers,
timeout=15,
reasoning_effort=effort_for_call(t_url, t_model, owner, sess),
)
title = title.strip().strip('"\'').strip()
@@ -1008,6 +1056,7 @@ async def build_chat_context(
chat_processor,
message: str,
session_id: str,
selected_notes_context: str | None = None,
preset_id=None,
att_ids: list = None,
use_web=None,
@@ -1180,6 +1229,23 @@ async def build_chat_context(
if search_context and allow_tool_preprocessing and not casual_low_signal:
preface.append(untrusted_context_message("prefetched search context", search_context))
# Inject selected notes as context
if selected_notes_context:
selected_notes_context = (
"The following selected notes and todos are reference context explicitly chosen "
"by the user. Use them directly when relevant to the user's request. Do not "
"ask for permission to use this context, and do not call note-management "
"tools just to retrieve the same notes; use those tools only for a requested "
"change or when this context is insufficient.\n\n"
+ selected_notes_context
)
preface.append(
untrusted_context_message(
"Selected notes and todos",
selected_notes_context,
)
)
# YouTube transcripts
for transcript in preprocessed.youtube_transcripts:
preface.append(untrusted_context_message("youtube transcript", transcript))
@@ -1324,13 +1390,17 @@ def _normalize_thinking(text: str) -> str:
# Handle garbled <think> tags: reasoning text followed by <think> as separator
# e.g. "The user said...I should respond.\n<think>Hey! What's up?"
# Linear form of `^([\s\S]+?)\n*<think>\s*([\s\S]*?)(?:</think>)?\s*$`:
# the lookbehind stops the lazy prefix re-scanning a newline run from every
# offset, and the optional trailing closer is dropped after the match
# instead of being retried at every body offset (both were quadratic).
garbled = re.match(
r'^([\s\S]+?)\n*<think(?:ing)?>\s*([\s\S]*?)(?:</think(?:ing)?>)?\s*$',
r'^([\s\S]+?)(?<![\s\S]\n)\n*<think(?:ing)?>\s*([\s\S]*)$',
text, re.IGNORECASE
)
if garbled:
before = garbled.group(1).strip()
after = garbled.group(2).strip()
after = re.sub(r'</think(?:ing)?>$', '', garbled.group(2).rstrip(), flags=re.IGNORECASE).strip()
# Only treat as garbled if the part before <think> looks like reasoning
reasoning_starts = (
'The user ', 'I need ', 'I should ', 'I will ',