Skip to content

Commit 9e88bfc

Browse files
committed
perf: optimize extract functions for streaming performance
1 parent 7f50b6f commit 9e88bfc

1 file changed

Lines changed: 49 additions & 79 deletions

File tree

packages/shared/src/index.ts

Lines changed: 49 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -432,96 +432,74 @@ export function stripThinking(text: string): string {
432432
* Robustly extract text from various AI response formats.
433433
*/
434434
export function extractText(obj: ExtractInput, includeReasoning = false): string {
435-
if (typeof obj === 'string') {
436-
return obj;
437-
}
438-
if (typeof obj !== 'object' || obj === null) {
439-
return '';
440-
}
435+
if (typeof obj === 'string') return obj;
436+
if (!obj || typeof obj !== 'object') return '';
441437

442-
const response = obj as Record<string, unknown>;
438+
const response = obj as any;
443439

444-
if (typeof response.response === 'string') return response.response;
445-
if (typeof response.text === 'string') return response.text;
446-
if (typeof response.content === 'string') return response.content;
440+
// Prioritize direct fields
441+
if (response.response && typeof response.response === 'string') return response.response;
442+
if (response.text && typeof response.text === 'string') return response.text;
443+
if (response.content && typeof response.content === 'string') return response.content;
447444

448445
// Handle delta objects (OpenAI-style streaming)
449-
if (typeof response.delta === 'object' && response.delta !== null) {
450-
const delta = response.delta as any;
451-
if (typeof delta.content === 'string') return delta.content;
446+
if (response.delta && typeof response.delta === 'object') {
447+
if (response.delta.content && typeof response.delta.content === 'string') return response.delta.content;
452448
if (includeReasoning) {
453-
if (typeof delta.reasoning_content === 'string') return delta.reasoning_content;
454-
if (typeof delta.thought === 'string') return delta.thought;
449+
if (response.delta.reasoning_content && typeof response.delta.reasoning_content === 'string') return response.delta.reasoning_content;
450+
if (response.delta.thought && typeof response.delta.thought === 'string') return response.delta.thought;
455451
}
456-
// Skip reasoning_content and thought in the final extraction to prevent leaks
457-
if (typeof delta.text === 'string') return delta.text;
452+
if (response.delta.text && typeof response.delta.text === 'string') return response.delta.text;
458453
}
459-
if (typeof response.delta === 'string') return response.delta;
460-
461-
// Recursively search in common nested fields
462-
if (Array.isArray(response.choices) && response.choices.length > 0)
463-
return extractText(response.choices[0] as ExtractInput, includeReasoning);
464-
if (response.message) return extractText(response.message as ExtractInput, includeReasoning);
465-
if (response.delta) return extractText(response.delta as ExtractInput, includeReasoning);
466-
if (response.tool_calls) return ''; // Skip tool calls in extraction
467-
if (Array.isArray(response.candidates) && response.candidates.length > 0)
468-
return extractText(response.candidates[0] as ExtractInput, includeReasoning);
469-
if (response.content) return extractText(response.content as ExtractInput, includeReasoning);
454+
if (response.delta && typeof response.delta === 'string') return response.delta;
455+
456+
// Nested fields
457+
if (response.choices?.[0]) {
458+
const choice = response.choices[0];
459+
if (choice.delta) return extractText(choice.delta, includeReasoning);
460+
if (choice.message) return extractText(choice.message, includeReasoning);
461+
if (choice.text && typeof choice.text === 'string') return choice.text;
462+
}
463+
464+
if (response.candidates?.[0]) return extractText(response.candidates[0].content, includeReasoning);
470465

471466
// Gemini parts
472-
if (Array.isArray(response.parts) && response.parts.length > 0) {
467+
if (Array.isArray(response.parts)) {
473468
let text = '';
474-
for (const part of response.parts as GeminiPart[]) {
475-
if (!includeReasoning && part.thought) continue; // Gemini thinking parts
469+
for (const part of response.parts) {
470+
if (!includeReasoning && part.thought) continue;
476471
if (part.text) text += part.text;
477472
}
478473
return text;
479474
}
480475

481-
// Any other string field as a fallback, but excluding known meta/thinking fields
482-
for (const key of Object.keys(response)) {
483-
if (['id', 'model', 'object', 'created', 'usage', 'index', 'finish_reason', 'reasoning_content', 'thought'].includes(key)) continue;
484-
if (typeof response[key] === 'string' && response[key]) return response[key] as string;
485-
}
486-
487476
return '';
488477
}
489478

490479
export function extractThinking(obj: ExtractInput): string {
491-
if (typeof obj !== 'object' || obj === null) return '';
492-
const response = obj as Record<string, unknown>;
480+
if (!obj || typeof obj !== 'object') return '';
481+
const response = obj as any;
493482

494-
if (typeof response.delta === 'object' && response.delta !== null) {
495-
const delta = response.delta as any;
496-
if (typeof delta.thought === 'string') return delta.thought;
497-
}
483+
if (response.delta?.thought && typeof response.delta.thought === 'string') return response.delta.thought;
498484

499-
if (Array.isArray(response.choices) && response.choices.length > 0) {
500-
const choice = response.choices[0] as any;
501-
if (choice.delta && typeof choice.delta.thought === 'string') return choice.delta.thought;
502-
if (choice.message && typeof choice.message.thought === 'string') return choice.message.thought;
503-
return extractThinking(choice as ExtractInput);
485+
if (response.choices?.[0]) {
486+
const choice = response.choices[0];
487+
if (choice.delta?.thought) return choice.delta.thought;
488+
if (choice.message?.thought) return choice.message.thought;
489+
return extractThinking(choice);
504490
}
505-
if (response.message) {
506-
if (typeof (response.message as any).thought === 'string') return (response.message as any).thought;
507-
return extractThinking(response.message as ExtractInput);
508-
}
509-
if (response.delta) return extractThinking(response.delta as ExtractInput);
510-
511-
if (Array.isArray(response.candidates) && response.candidates.length > 0) {
512-
const candidate = response.candidates[0] as any;
513-
if (candidate.content && Array.isArray(candidate.content.parts)) {
514-
let thinking = '';
515-
for (const part of candidate.content.parts as GeminiPart[]) {
516-
if (part.thought && part.text) thinking += part.text;
517-
}
518-
return thinking;
491+
492+
if (response.candidates?.[0]?.content?.parts) {
493+
let thinking = '';
494+
for (const part of response.candidates[0].content.parts) {
495+
if (part.thought && part.text) thinking += part.text;
519496
}
497+
return thinking;
520498
}
521499

522500
if (Array.isArray(response.parts)) {
523501
let thinking = '';
524-
for (const part of response.parts as GeminiPart[]) {
502+
for (const part of response.parts) {
525503
if (part.thought && part.text) thinking += part.text;
526504
}
527505
return thinking;
@@ -531,25 +509,17 @@ export function extractThinking(obj: ExtractInput): string {
531509
}
532510

533511
export function extractReasoning(obj: ExtractInput): string {
534-
if (typeof obj !== 'object' || obj === null) return '';
535-
const response = obj as Record<string, unknown>;
512+
if (!obj || typeof obj !== 'object') return '';
513+
const response = obj as any;
536514

537-
if (typeof response.delta === 'object' && response.delta !== null) {
538-
const delta = response.delta as any;
539-
if (typeof delta.reasoning_content === 'string') return delta.reasoning_content;
540-
}
515+
if (response.delta?.reasoning_content && typeof response.delta.reasoning_content === 'string') return response.delta.reasoning_content;
541516

542-
if (Array.isArray(response.choices) && response.choices.length > 0) {
543-
const choice = response.choices[0] as any;
544-
if (choice.delta && typeof choice.delta.reasoning_content === 'string') return choice.delta.reasoning_content;
545-
if (choice.message && typeof choice.message.reasoning_content === 'string') return choice.message.reasoning_content;
546-
return extractReasoning(choice as ExtractInput);
547-
}
548-
if (response.message) {
549-
if (typeof (response.message as any).reasoning_content === 'string') return (response.message as any).reasoning_content;
550-
return extractReasoning(response.message as ExtractInput);
517+
if (response.choices?.[0]) {
518+
const choice = response.choices[0];
519+
if (choice.delta?.reasoning_content) return choice.delta.reasoning_content;
520+
if (choice.message?.reasoning_content) return choice.message.reasoning_content;
521+
return extractReasoning(choice);
551522
}
552-
if (response.delta) return extractReasoning(response.delta as ExtractInput);
553523

554524
return '';
555525
}

0 commit comments

Comments
 (0)