v4.1.6: SMILES 구조식 위젯 교과서식 개편 + 화학표기 규칙 추가 + 코드블록 렌더링 버그 수정
- SMILES 위젯을 버튼 클릭식 큰 위젯 → 자동렌더링 소형 인라인(교과서식)으로 전면 개편 - 링크/이미지/볼드 등 후속 정규식이 SMILES 입체화학 표기([C@@H](C) 등)를 마크다운 링크로 오인해 구조식이 깨지던 버그 수정 (코드블록 결과물을 플레이스홀더로 보호 후 최종 복원) - 구조 복잡도(중원자 수)에 따라 SMILES 캔버스 크기 동적 조절 (벤젠은 작게, 콜레스테롤 등은 크게) - SMILES 위젯 테두리/배경/여백 제거로 더 가벼운 인라인 표시 - 코드블록(.md-code-block) 검은 배경 제거 + 고정폭 폰트 누락으로 ASCII 다이어그램이 틀어지던 버그 수정 (font-family 미지정 → 본문 가변폭 폰트 상속이 원인) - code.trim()이 전체 문자열 기준으로만 동작해 ASCII 아트 첫 줄의 들여쓰기만 사라지던 버그 수정 (줄 단위가 아닌 blank 줄만 제거하도록 변경) - 시스템 프롬프트에 CHEMISTRY NOTATION 규칙 추가 (ASCII 아트 금지, LaTeX/mhchem·SMILES 사용 유도) - Ollama Cloud 일시적 오류(5xx/타임아웃) 발생 시 즉시 실패 대신 백오프 재시도 추가 - weather_map_screenshot 툴을 ToolRegistry에도 등록 (buildImageMarkdown export 추가) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5785,6 +5785,7 @@ async function handleChat(
|
||||
content: isTranslateSession ? `You are a medical translator. Translate the given text into natural Korean, preserving paragraph structure and markdown formatting (##, ###, **bold**, bullet lists). Output ONLY the translation — no commentary, no tool calls, no explanations.` : isProjSession ? `You are a project file designer. Output ONLY the project-files JSON block as instructed. No tool calls. No extra text.` : `${executionModeSystemBlock ? `${executionModeSystemBlock}\n\n` : ''}You are SmallClaw 🦞, a local AI assistant.\nCurrent date: ${dateStr}, ${timeStr}.\nNever search for or link SmallClaw repos unless the user is asking about SmallClaw itself.\nThis app runs on the user's own machine — browser/desktop automation requests are pre-authorized.\nKeep responses SHORT (1-2 sentences). Don't think out loud. Act and report. Greet naturally without tools.
|
||||
ANTI-HALLUCINATION: When a tool returns a result, report EXACTLY what the tool returned — never contradict or ignore tool output. If a tool says "(no rows)", say so. Never invent data, file contents, table names, or command output. If you don't know something, call a tool to find out or say you don't know.
|
||||
IMAGE EDITING RULE: NEVER call image_edit (or any editing tool) when a user uploads a photo without explicitly requesting edits. Uploading a photo is NOT a request to edit it. Only call image_edit when the user's message explicitly asks for an edit (e.g. "수채화로 바꿔줘", "회전해줘"). Violating this rule is a critical error.${browserRuleBlock}
|
||||
CHEMISTRY NOTATION: NEVER draw molecular structures as ASCII art (H/C/#/=/\\/| characters arranged to look like a diagram) — it always renders as garbled, misaligned text. Instead: for a formula or reaction, use LaTeX inside $...$ (e.g. $\\ce{C4H10}$, $\\ce{2H2 + O2 -> 2H2O}$ — mhchem extension is loaded). For an actual 2D structure with real bond lines (rings, branches), output a \`\`\`smiles\`\`\` code block containing the SMILES string (e.g. \`\`\`smiles\\nc1ccccc1\\n\`\`\` for benzene, \`\`\`smiles\\nCC(=O)Oc1ccccc1C(=O)O\\n\`\`\` for aspirin) — the client automatically renders it as a proper 2D diagram with bond lines.
|
||||
CODE OUTPUT: When writing code in a fenced code block, start with a filename comment on line 1: \`# filename: snake_game.py\` (Python), \`// filename: app.js\` (JS/C), \`<!-- filename: index.html -->\` (HTML). Never repeat code already written in this conversation. For modifications to existing files, use coder_overwrite_lines or coder_insert_lines (not coder_write_file — it only works for NEW files). All code changes are presented as diffs for the user to review before being applied. Write code directly — do not ask for permission.
|
||||
PACKAGE INSTALL: NEVER run pip install, npm install, apt-get, or any package installation command. If a package is missing, just write the code and mention the package name in a comment — let the user decide whether to install it. Do NOT attempt to install packages yourself.
|
||||
OUTPUT FORMAT: When presenting 3+ items (news articles, emails, search results, lists), always use a markdown table or structured bullet list with clear headers. Never dump them as a long paragraph. Example: news → table with columns 제목|요약|출처.${callerContext ? '\n\n' + callerContext : ''}${browserStateCtx}${personalityCtx}${skillsManager.buildPromptContextForUser(username ? getUserWorkspace(username) : null, 16000, message)}`,
|
||||
@@ -6767,6 +6768,16 @@ RULES:
|
||||
|
||||
sendSSE('info', { message: 'Thinking...' });
|
||||
|
||||
// Transient cloud-provider errors (Ollama Cloud 500s, timeouts, connection resets) are
|
||||
// currently surfaced straight to the user as `Error: ...` with zero retry — a brief
|
||||
// provider-side blip becomes the entire assistant reply. Retry a couple of times with a
|
||||
// short backoff before giving up; a separate counter from MAX_TOOL_ROUNDS so retries
|
||||
// don't eat into the model's legitimate tool-calling budget.
|
||||
let transientErrorRetryCount = 0;
|
||||
const MAX_TRANSIENT_ERROR_RETRIES = 2;
|
||||
const isTransientProviderError = (msg: string): boolean =>
|
||||
/internal server error|bad gateway|service unavailable|gateway timeout|\b5\d{2}\b|econnreset|etimedout|socket hang up|fetch failed/i.test(String(msg || ''));
|
||||
|
||||
for (let round = 0; ; round++) {
|
||||
if (round >= MAX_TOOL_ROUNDS) {
|
||||
const allowExtendedFileOpLoop =
|
||||
@@ -7184,6 +7195,13 @@ RULES:
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('[v2] Chat error:', err.message);
|
||||
if (isTransientProviderError(err.message) && transientErrorRetryCount < MAX_TRANSIENT_ERROR_RETRIES) {
|
||||
transientErrorRetryCount++;
|
||||
const backoffMs = 1500 * transientErrorRetryCount;
|
||||
sendSSE('info', { message: `Provider error, retrying (${transientErrorRetryCount}/${MAX_TRANSIENT_ERROR_RETRIES})...` });
|
||||
await new Promise(r => setTimeout(r, backoffMs));
|
||||
continue;
|
||||
}
|
||||
return { type: 'chat', text: `Error: ${err.message}` };
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ function runPython(script: string, timeoutMs = 60_000): Promise<any> {
|
||||
});
|
||||
}
|
||||
|
||||
function buildImageMarkdown(filePath: string, workspacePath: string): string {
|
||||
export function buildImageMarkdown(filePath: string, workspacePath: string): string {
|
||||
const relPath = path.relative(workspacePath, filePath).replace(/\\/g, '/');
|
||||
const baseName = path.basename(filePath);
|
||||
return ``;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { imageReadTool, imagePreviewTool, imageInfoTool, imageEditTool } from '.
|
||||
import { audioTranscribeTool } from './audio-transcribe.js';
|
||||
import { pythonEvalTool } from './python.js';
|
||||
import { sqliteTool } from './sqlite.js';
|
||||
import { weatherSearchTool, weatherAirPollutionTool, weatherOpenMeteoTool, weatherKmaTool, weatherAirKoreaTool, weatherNasaPowerTool, weatherEra5Tool, weatherCdsTool, weatherCmip6Tool } from './weather.js';
|
||||
import { weatherSearchTool, weatherAirPollutionTool, weatherOpenMeteoTool, weatherKmaTool, weatherAirKoreaTool, weatherNasaPowerTool, weatherEra5Tool, weatherCdsTool, weatherCmip6Tool, weatherMapScreenshotTool } from './weather.js';
|
||||
import { emailListTool, emailReadTool, emailSendTool, emailSearchTool, emailDeleteTool } from './email.js';
|
||||
import { kakaoSendTool } from './kakao.js';
|
||||
import { koreanLawSearchTool, koreanLawFetchTool, usCaseSearchTool } from './legal.js';
|
||||
@@ -217,6 +217,7 @@ class ToolRegistry {
|
||||
this.registerSafe(weatherEra5Tool);
|
||||
this.registerSafe(weatherCdsTool);
|
||||
this.registerSafe(weatherCmip6Tool);
|
||||
this.registerSafe(weatherMapScreenshotTool);
|
||||
// Email tools (IMAP + SMTP)
|
||||
this.registerSafe(emailListTool);
|
||||
this.registerSafe(emailReadTool);
|
||||
|
||||
Reference in New Issue
Block a user