import { ResponsesAPIOutput, Source } from '../types/ai.types'; /** * Parse JSON response from AI, handling markdown code blocks */ export function parseJSONResponse(response: string): T { const cleaned = response .replace(/```json\n?/g, '') .replace(/```\n?/g, '') .trim(); return JSON.parse(cleaned); } /** * Extract source citations from chat completion annotations */ export function extractSourceCitations(completion: any): Source[] { const sources: Source[] = []; if (completion.choices?.[0]?.message?.annotations) { const annotations = completion.choices[0].message.annotations as any[]; for (const annotation of annotations) { if (annotation.type === 'url_citation' && annotation.url_citation) { sources.push({ title: annotation.url_citation.title || 'Source', url: annotation.url_citation.url, }); } } } return sources; } /** * Parse output from Responses API */ export function parseResponsesAPIOutput(response: ResponsesAPIOutput): string { // Try direct output_text field first if (typeof response.output_text === 'string' && response.output_text.length > 0) { return response.output_text; } // Fallback to parsing output array if (Array.isArray(response.output)) { const msg = response.output.find((o: any) => o.type === 'message'); if (msg && Array.isArray(msg.content)) { const textPart = msg.content.find((c: any) => c.type === 'output_text'); if (textPart?.text) { return textPart.text; } } } return ''; } /** * Strip HTML tags from content */ export function stripHtmlTags(html: string): string { return html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); }