Download internal/js/chat-stream/sse_parse_impl.js from huggeu/ds2api: direct link, hf CLI and curl.
- Browser
- Download file 16.7 kB
-
https://huggingface.co/spaces/huggeu/ds2api/resolve/main/internal/js/chat-stream/sse_parse_impl.js
- Command line
-
hf download hf://spaces/huggeu/ds2api/internal/js/chat-stream/sse_parse_impl.js
-
curl -L -o sse_parse_impl.js https://huggingface.co/spaces/huggeu/ds2api/resolve/main/internal/js/chat-stream/sse_parse_impl.js
16.7 kB
| ; | |
| // Implementation moved here to keep the line-gate wrapper tiny. | |
| const { | |
| SKIP_PATTERNS, | |
| SKIP_EXACT_PATHS, | |
| } = require('../shared/deepseek-constants'); | |
| function stripThinkTags(text) { | |
| if (typeof text !== 'string' || !text) { | |
| return text; | |
| } | |
| return text.replace(/<\/?\s*think\s*>/gi, ''); | |
| } | |
| function splitThinkingParts(parts) { | |
| const out = []; | |
| let thinkingDone = false; | |
| for (const p of parts) { | |
| if (!p) continue; | |
| if (thinkingDone && p.type === 'thinking') { | |
| const cleaned = stripThinkTags(p.text); | |
| if (cleaned) { | |
| out.push({ text: cleaned, type: 'text' }); | |
| } | |
| continue; | |
| } | |
| if (p.type !== 'thinking') { | |
| const cleaned = stripThinkTags(p.text); | |
| if (cleaned) { | |
| out.push({ text: cleaned, type: p.type }); | |
| } | |
| continue; | |
| } | |
| const match = /<\/\s*think\s*>/i.exec(p.text); | |
| if (!match) { | |
| out.push(p); | |
| continue; | |
| } | |
| thinkingDone = true; | |
| const before = p.text.substring(0, match.index); | |
| let after = p.text.substring(match.index + match[0].length); | |
| if (before) { | |
| out.push({ text: before, type: 'thinking' }); | |
| } | |
| after = stripThinkTags(after); | |
| if (after) { | |
| out.push({ text: after, type: 'text' }); | |
| } | |
| } | |
| return { parts: out, transitioned: thinkingDone }; | |
| } | |
| function dropThinkingParts(parts) { | |
| if (!Array.isArray(parts) || parts.length === 0) { | |
| return parts; | |
| } | |
| return parts.filter((p) => p && p.type !== 'thinking'); | |
| } | |
| function finalizeThinkingParts(parts, thinkingEnabled, newType) { | |
| const splitResult = splitThinkingParts(parts); | |
| let finalType = newType; | |
| let finalParts = splitResult.parts; | |
| if (splitResult.transitioned) { | |
| finalType = 'text'; | |
| } | |
| if (!thinkingEnabled) { | |
| finalParts = dropThinkingParts(finalParts); | |
| } | |
| return { parts: finalParts, newType: finalType }; | |
| } | |
| function parseChunkForContent(chunk, thinkingEnabled, currentType, stripReferenceMarkers = true) { | |
| if (!chunk || typeof chunk !== 'object') { | |
| return { | |
| parsed: false, | |
| parts: [], | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| outputTokens: 0, | |
| newType: currentType, | |
| }; | |
| } | |
| const usage = extractAccumulatedTokenUsage(chunk); | |
| const promptTokens = usage.prompt; | |
| const outputTokens = usage.output; | |
| if (Object.prototype.hasOwnProperty.call(chunk, 'error')) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: true, | |
| contentFilter: false, | |
| errorMessage: formatErrorMessage(chunk.error), | |
| promptTokens, | |
| outputTokens, | |
| newType: currentType, | |
| }; | |
| } | |
| const pathValue = asString(chunk.p); | |
| if (hasContentFilterStatus(chunk)) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: true, | |
| contentFilter: true, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: currentType, | |
| }; | |
| } | |
| if (shouldSkipPath(pathValue)) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: currentType, | |
| }; | |
| } | |
| if (isStatusPath(pathValue)) { | |
| if (isFinishedStatus(chunk.v)) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: true, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: currentType, | |
| }; | |
| } | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: currentType, | |
| }; | |
| } | |
| if (!Object.prototype.hasOwnProperty.call(chunk, 'v')) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: currentType, | |
| }; | |
| } | |
| let newType = currentType; | |
| const parts = []; | |
| if (pathValue === 'response/fragments' && asString(chunk.o).toUpperCase() === 'APPEND' && Array.isArray(chunk.v)) { | |
| for (const frag of chunk.v) { | |
| if (!frag || typeof frag !== 'object') { | |
| continue; | |
| } | |
| const fragType = asString(frag.type).toUpperCase(); | |
| const content = asContentString(frag.content, stripReferenceMarkers); | |
| if (!content) { | |
| continue; | |
| } | |
| if (fragType === 'THINK' || fragType === 'THINKING') { | |
| newType = 'thinking'; | |
| parts.push({ text: content, type: 'thinking' }); | |
| } else if (fragType === 'RESPONSE') { | |
| newType = 'text'; | |
| parts.push({ text: content, type: 'text' }); | |
| } else { | |
| parts.push({ text: content, type: 'text' }); | |
| } | |
| } | |
| } | |
| if (pathValue === 'response' && Array.isArray(chunk.v)) { | |
| for (const item of chunk.v) { | |
| if (!item || typeof item !== 'object') { | |
| continue; | |
| } | |
| if (item.p === 'fragments' && item.o === 'APPEND' && Array.isArray(item.v)) { | |
| for (const frag of item.v) { | |
| const fragType = asString(frag && frag.type).toUpperCase(); | |
| if (fragType === 'THINK' || fragType === 'THINKING') { | |
| newType = 'thinking'; | |
| } else if (fragType === 'RESPONSE') { | |
| newType = 'text'; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (pathValue === 'response/content') { | |
| newType = 'text'; | |
| } else if (pathValue === 'response/thinking_content' && (!thinkingEnabled || newType !== 'text')) { | |
| newType = 'thinking'; | |
| } | |
| let partType = 'text'; | |
| if (pathValue === 'response/thinking_content') { | |
| if (!thinkingEnabled) { | |
| partType = 'thinking'; | |
| } else if (newType === 'text') { | |
| partType = 'text'; | |
| } else { | |
| partType = 'thinking'; | |
| } | |
| } else if (pathValue === 'response/content') { | |
| partType = 'text'; | |
| } else if (pathValue.includes('response/fragments') && pathValue.includes('/content')) { | |
| partType = newType; | |
| } else if (!pathValue) { | |
| partType = newType || 'text'; | |
| } | |
| const val = chunk.v; | |
| if (typeof val === 'string') { | |
| if (isFinishedStatus(val) && (!pathValue || pathValue === 'status')) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: true, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType, | |
| }; | |
| } | |
| if (isStatusPath(pathValue)) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType, | |
| }; | |
| } | |
| const content = asContentString(val, stripReferenceMarkers); | |
| if (content) { | |
| parts.push({ text: content, type: partType }); | |
| } | |
| let resolvedParts = filterLeakedContentFilterParts(parts); | |
| const finalized = finalizeThinkingParts(resolvedParts, thinkingEnabled, newType); | |
| return { | |
| parsed: true, | |
| parts: finalized.parts, | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: finalized.newType, | |
| }; | |
| } | |
| if (Array.isArray(val)) { | |
| const extracted = extractContentRecursive(val, partType, stripReferenceMarkers); | |
| if (extracted.finished) { | |
| return { | |
| parsed: true, | |
| parts: [], | |
| finished: true, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType, | |
| }; | |
| } | |
| parts.push(...extracted.parts); | |
| let resolvedParts = filterLeakedContentFilterParts(parts); | |
| const finalized = finalizeThinkingParts(resolvedParts, thinkingEnabled, newType); | |
| return { | |
| parsed: true, | |
| parts: finalized.parts, | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: finalized.newType, | |
| }; | |
| } | |
| if (val && typeof val === 'object') { | |
| const directContent = asContentString(val, stripReferenceMarkers); | |
| if (directContent) { | |
| parts.push({ text: directContent, type: partType }); | |
| } | |
| const resp = val.response && typeof val.response === 'object' ? val.response : val; | |
| if (Array.isArray(resp.fragments)) { | |
| for (const frag of resp.fragments) { | |
| if (!frag || typeof frag !== 'object') { | |
| continue; | |
| } | |
| const content = asContentString(frag.content, stripReferenceMarkers); | |
| if (!content) { | |
| continue; | |
| } | |
| const t = asString(frag.type).toUpperCase(); | |
| if (t === 'THINK' || t === 'THINKING') { | |
| newType = 'thinking'; | |
| parts.push({ text: content, type: 'thinking' }); | |
| } else if (t === 'RESPONSE') { | |
| newType = 'text'; | |
| parts.push({ text: content, type: 'text' }); | |
| } else { | |
| parts.push({ text: content, type: partType }); | |
| } | |
| } | |
| } | |
| } | |
| let resolvedParts = filterLeakedContentFilterParts(parts); | |
| const finalized = finalizeThinkingParts(resolvedParts, thinkingEnabled, newType); | |
| return { | |
| parsed: true, | |
| parts: finalized.parts, | |
| finished: false, | |
| contentFilter: false, | |
| errorMessage: '', | |
| promptTokens, | |
| outputTokens, | |
| newType: finalized.newType, | |
| }; | |
| } | |
| function extractContentRecursive(items, defaultType, stripReferenceMarkers = true) { | |
| const parts = []; | |
| for (const it of items) { | |
| if (!it || typeof it !== 'object') { | |
| continue; | |
| } | |
| if (!Object.prototype.hasOwnProperty.call(it, 'v')) { | |
| continue; | |
| } | |
| const itemPath = asString(it.p); | |
| const itemV = it.v; | |
| if (isStatusPath(itemPath)) { | |
| if (isFinishedStatus(itemV)) { | |
| return { parts: [], finished: true }; | |
| } | |
| continue; | |
| } | |
| if (shouldSkipPath(itemPath)) { | |
| continue; | |
| } | |
| const content = asContentString(it.content, stripReferenceMarkers); | |
| if (content) { | |
| const typeName = asString(it.type).toUpperCase(); | |
| if (typeName === 'THINK' || typeName === 'THINKING') { | |
| parts.push({ text: content, type: 'thinking' }); | |
| } else if (typeName === 'RESPONSE') { | |
| parts.push({ text: content, type: 'text' }); | |
| } else { | |
| parts.push({ text: content, type: defaultType }); | |
| } | |
| continue; | |
| } | |
| let partType = defaultType; | |
| if (itemPath.includes('thinking')) { | |
| partType = 'thinking'; | |
| } else if (itemPath.includes('content') || itemPath === 'response' || itemPath === 'fragments') { | |
| partType = 'text'; | |
| } | |
| if (typeof itemV === 'string') { | |
| if (isStatusPath(itemPath)) { | |
| continue; | |
| } | |
| if (itemV && itemV !== 'FINISHED') { | |
| const content = asContentString(itemV, stripReferenceMarkers); | |
| if (content) { | |
| parts.push({ text: content, type: partType }); | |
| } | |
| } | |
| continue; | |
| } | |
| if (!Array.isArray(itemV)) { | |
| continue; | |
| } | |
| for (const inner of itemV) { | |
| if (typeof inner === 'string') { | |
| if (inner) { | |
| const content = asContentString(inner, stripReferenceMarkers); | |
| if (content) { | |
| parts.push({ text: content, type: partType }); | |
| } | |
| } | |
| continue; | |
| } | |
| if (!inner || typeof inner !== 'object') { | |
| continue; | |
| } | |
| const ct = asContentString(inner.content, stripReferenceMarkers); | |
| if (!ct) { | |
| continue; | |
| } | |
| const typeName = asString(inner.type).toUpperCase(); | |
| if (typeName === 'THINK' || typeName === 'THINKING') { | |
| parts.push({ text: ct, type: 'thinking' }); | |
| } else if (typeName === 'RESPONSE') { | |
| parts.push({ text: ct, type: 'text' }); | |
| } else { | |
| parts.push({ text: ct, type: partType }); | |
| } | |
| } | |
| } | |
| return { parts, finished: false }; | |
| } | |
| function isStatusPath(pathValue) { | |
| return pathValue === 'response/status' || pathValue === 'status'; | |
| } | |
| function isFinishedStatus(value) { | |
| return asString(value).toUpperCase() === 'FINISHED'; | |
| } | |
| function filterLeakedContentFilterParts(parts) { | |
| if (!Array.isArray(parts) || parts.length === 0) { | |
| return parts; | |
| } | |
| const out = []; | |
| for (const p of parts) { | |
| if (!p || typeof p !== 'object') { | |
| continue; | |
| } | |
| const { text, stripped } = stripLeakedContentFilterSuffix(p.text); | |
| if (stripped && shouldDropCleanedLeakedChunk(text)) { | |
| continue; | |
| } | |
| if (stripped) { | |
| out.push({ ...p, text }); | |
| continue; | |
| } | |
| out.push(p); | |
| } | |
| return out; | |
| } | |
| function stripLeakedContentFilterSuffix(text) { | |
| if (typeof text !== 'string' || text === '') { | |
| return { text, stripped: false }; | |
| } | |
| const upperText = text.toUpperCase(); | |
| const idx = upperText.indexOf('CONTENT_FILTER'); | |
| if (idx < 0) { | |
| return { text, stripped: false }; | |
| } | |
| return { | |
| text: text.slice(0, idx).replace(/[ \t\r]+$/g, ''), | |
| stripped: true, | |
| }; | |
| } | |
| function shouldDropCleanedLeakedChunk(cleaned) { | |
| if (cleaned === '') { | |
| return true; | |
| } | |
| if (typeof cleaned === 'string' && cleaned.includes('\n')) { | |
| return false; | |
| } | |
| return asString(cleaned).trim() === ''; | |
| } | |
| function hasContentFilterStatus(chunk) { | |
| if (!chunk || typeof chunk !== 'object') { | |
| return false; | |
| } | |
| const code = asString(chunk.code); | |
| if (code && code.toLowerCase() === 'content_filter') { | |
| return true; | |
| } | |
| return hasContentFilterStatusValue(chunk); | |
| } | |
| function hasContentFilterStatusValue(v) { | |
| if (Array.isArray(v)) { | |
| for (const item of v) { | |
| if (hasContentFilterStatusValue(item)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| if (!v || typeof v !== 'object') { | |
| return false; | |
| } | |
| const pathValue = asString(v.p); | |
| if (pathValue && pathValue.toLowerCase().includes('status')) { | |
| if (asString(v.v).toLowerCase() === 'content_filter') { | |
| return true; | |
| } | |
| } | |
| if (asString(v.code).toLowerCase() === 'content_filter') { | |
| return true; | |
| } | |
| for (const value of Object.values(v)) { | |
| if (hasContentFilterStatusValue(value)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| function extractAccumulatedTokenUsage(chunk) { | |
| // 临时策略:忽略上游 usage 字段(accumulated_token_usage / token_usage), | |
| // 统一使用内部估算计数,避免上下文累计口径误差。 | |
| void chunk; | |
| return { prompt: 0, output: 0 }; | |
| } | |
| function formatErrorMessage(v) { | |
| if (typeof v === 'string') { | |
| return v; | |
| } | |
| if (v == null) { | |
| return String(v); | |
| } | |
| try { | |
| return JSON.stringify(v); | |
| } catch (_err) { | |
| return String(v); | |
| } | |
| } | |
| function shouldSkipPath(pathValue) { | |
| if (isFragmentStatusPath(pathValue)) { | |
| return true; | |
| } | |
| if (SKIP_EXACT_PATHS.has(pathValue)) { | |
| return true; | |
| } | |
| for (const p of SKIP_PATTERNS) { | |
| if (pathValue.includes(p)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| function isFragmentStatusPath(pathValue) { | |
| if (!pathValue || pathValue === 'response/status') { | |
| return false; | |
| } | |
| return /^response\/fragments\/-?\d+\/status$/i.test(pathValue); | |
| } | |
| function isCitation(text) { | |
| return asString(text).trim().startsWith('[citation:'); | |
| } | |
| function asContentString(v, stripReferenceMarkers = true) { | |
| if (typeof v === 'string') { | |
| return stripReferenceMarkers ? stripReferenceMarkersText(v) : v; | |
| } | |
| if (Array.isArray(v)) { | |
| let out = ''; | |
| for (const item of v) { | |
| out += asContentString(item, stripReferenceMarkers); | |
| } | |
| return out; | |
| } | |
| if (v && typeof v === 'object') { | |
| if (Object.prototype.hasOwnProperty.call(v, 'content')) { | |
| return asContentString(v.content, stripReferenceMarkers); | |
| } | |
| if (Object.prototype.hasOwnProperty.call(v, 'v')) { | |
| return asContentString(v.v, stripReferenceMarkers); | |
| } | |
| if (Object.prototype.hasOwnProperty.call(v, 'text')) { | |
| return asContentString(v.text, stripReferenceMarkers); | |
| } | |
| if (Object.prototype.hasOwnProperty.call(v, 'value')) { | |
| return asContentString(v.value, stripReferenceMarkers); | |
| } | |
| return ''; | |
| } | |
| if (v == null) { | |
| return ''; | |
| } | |
| const text = String(v); | |
| return stripReferenceMarkers ? stripReferenceMarkersText(text) : text; | |
| } | |
| function stripReferenceMarkersText(text) { | |
| if (!text) { | |
| return text; | |
| } | |
| return text.replace(/\[(?:citation|reference):\s*\d+\]/gi, ''); | |
| } | |
| function asString(v) { | |
| if (typeof v === 'string') { | |
| return v.trim(); | |
| } | |
| if (Array.isArray(v)) { | |
| return asString(v[0]); | |
| } | |
| if (v == null) { | |
| return ''; | |
| } | |
| return String(v).trim(); | |
| } | |
| module.exports = { | |
| parseChunkForContent, | |
| extractContentRecursive, | |
| filterLeakedContentFilterParts, | |
| hasContentFilterStatus, | |
| extractAccumulatedTokenUsage, | |
| shouldSkipPath, | |
| isFragmentStatusPath, | |
| isCitation, | |
| stripReferenceMarkers: stripReferenceMarkersText, | |
| stripThinkTags, | |
| }; | |