| 1 | export const extractModelText = (value: unknown): string => { |
| 2 | if (typeof value === 'string') return value |
| 3 | if (!value || typeof value !== 'object') return '' |
| 4 | const content = 'content' in value ? (value as { content?: unknown }).content : undefined |
| 5 | if (typeof content === 'string') return content |
| 6 | if (Array.isArray(content)) { |
| 7 | return content |
| 8 | .map((item) => { |
| 9 | if (typeof item === 'string') return item |
| 10 | if (item && typeof item === 'object' && 'text' in item) { |
| 11 | return typeof (item as { text?: unknown }).text === 'string' |
| 12 | ? String((item as { text?: unknown }).text) |
| 13 | : '' |
| 14 | } |
| 15 | return '' |
| 16 | }) |
| 17 | .join('\n') |
| 18 | .trim() |
| 19 | } |
| 20 | return '' |
| 21 | } |
| 22 | |
| 23 | export const extractJsonBlock = (raw: string): string => { |
| 24 | const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i) |
| 25 | if (fenced?.[1]) return fenced[1].trim() |
| 26 | |
| 27 | const extractBalanced = ( |
| 28 | start: number, |
| 29 | open: '{' | '[', |
| 30 | close: '}' | ']' |
| 31 | ): string | null => { |
| 32 | let depth = 0 |
| 33 | let inString = false |
| 34 | let escaped = false |
| 35 | |
| 36 | for (let index = start; index < raw.length; index += 1) { |
| 37 | const char = raw[index] |
| 38 | if (inString) { |
| 39 | if (escaped) { |
| 40 | escaped = false |
| 41 | } else if (char === '\\') { |
| 42 | escaped = true |
| 43 | } else if (char === '"') { |
| 44 | inString = false |
| 45 | } |
| 46 | continue |
| 47 | } |
| 48 | if (char === '"') { |
| 49 | inString = true |
| 50 | continue |
| 51 | } |
| 52 | if (char === open) depth += 1 |
| 53 | if (char === close) { |
| 54 | depth -= 1 |
| 55 | if (depth === 0) return raw.slice(start, index + 1) |
| 56 | } |
| 57 | } |
| 58 | return null |
| 59 | } |
| 60 | |
| 61 | for (let start = 0; start < raw.length; start += 1) { |
| 62 | const char = raw[start] |
| 63 | const block = |
| 64 | char === '{' |
| 65 | ? extractBalanced(start, '{', '}') |
| 66 | : char === '[' |
| 67 | ? extractBalanced(start, '[', ']') |
| 68 | : null |
| 69 | if (!block) continue |
| 70 | try { |
| 71 | JSON.parse(block) |
| 72 | return block.trim() |
| 73 | } catch { |
| 74 | // This can be a prose bracket or malformed model output; keep scanning. |
| 75 | } |
| 76 | } |
| 77 | return raw.trim() |
| 78 | } |
| 79 |