MCPcopy
hub / github.com/Doorman11991/smallcode / chatCompletion

Function chatCompletion

bin/smallcode.js:2128–2555  ·  view source on GitHub ↗
(config, messages)

Source from the content-addressed store, hash-verified

2126
2127// Make a chat completion request (non-streaming for tool use, streaming for final response)
2128async function chatCompletion(config, messages) {
2129 let target = config.activeModelTarget || getModelTarget(config, 'default');
2130 let requestConfig = withModelTarget(config, target);
2131 let baseUrl = target.baseUrl;
2132 const systemMsg = {
2133 role: 'system',
2134 content: buildCompactSystemPrompt(currentTaskType, messages),
2135 };
2136
2137 try {
2138 // Strip ANSI escape codes from all message content before sending to model.
2139 // Thinking models (Qwen3, etc.) will reproduce ANSI codes they see in context,
2140 // causing corrupted bash commands like "find ... -\x1b[38;2mtype f".
2141 function stripAnsiFromMsg(msg) {
2142 if (!msg || typeof msg.content !== 'string') return msg;
2143 return { ...msg, content: msg.content.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '').replace(/\x1b\][^\x07]*\x07/g, '') };
2144 }
2145 const processedMessages = messages.map(stripAnsiFromMsg);
2146
2147 // Cache-split (Feature #14): when SMALLCODE_CACHE_SPLIT=true, dynamic
2148 // context (memory, knowledge) is moved out of the system prompt and into
2149 // a [CONTEXT] block prepended to the latest user message. This keeps the
2150 // system prompt stable across turns so remote APIs with prefix caching
2151 // (Anthropic, OpenAI) get cache hits on the static portion.
2152 const dynamicCtx = buildDynamicContext(messages);
2153 if (dynamicCtx) {
2154 const lastIdx = processedMessages.reduce((last, m, i) => m.role === 'user' ? i : last, -1);
2155 if (lastIdx >= 0) {
2156 const lastMsg = processedMessages[lastIdx];
2157 if (typeof lastMsg.content === 'string') {
2158 processedMessages[lastIdx] = {
2159 ...lastMsg,
2160 content: dynamicCtx + lastMsg.content,
2161 };
2162 }
2163 // If last user message is multimodal (image array), prepend as first text element
2164 else if (Array.isArray(lastMsg.content)) {
2165 const firstText = lastMsg.content.find(c => c.type === 'text');
2166 if (firstText) {
2167 processedMessages[lastIdx] = {
2168 ...lastMsg,
2169 content: [
2170 { type: 'text', text: dynamicCtx + firstText.text },
2171 ...lastMsg.content.filter(c => c !== firstText),
2172 ],
2173 };
2174 }
2175 }
2176 }
2177 }
2178
2179 // Transform messages with images into multimodal format
2180 // OPTIMIZATION: Only re-extract images from the LAST user message (the new one).
2181 // Older messages that had images already had their content consumed; re-reading
2182 // the image from disk on every call is both wasteful (disk I/O) and causes
2183 // context overflow (a 1MB PNG = ~330k base64 tokens sent on EVERY API call).
2184 const { extractImages, formatImagesForAPI, modelSupportsVision } = require('../src/session/images');
2185 const lastUserIdx = processedMessages.length > 0

Callers 1

runAgentLoopFunction · 0.70

Calls 15

getModelTargetFunction · 0.85
withModelTargetFunction · 0.85
buildCompactSystemPromptFunction · 0.85
buildDynamicContextFunction · 0.85
extractImagesFunction · 0.85
modelSupportsVisionFunction · 0.85
formatImagesForAPIFunction · 0.85
getExecutorModelFunction · 0.85
getAdaptiveRouterFunction · 0.85
getModelTargetForModelFunction · 0.85
applyThinkingBudgetFunction · 0.85

Tested by

no test coverage detected