From 47519f3744e7a44c52451ad2bde3bab6248ac121 Mon Sep 17 00:00:00 2001 From: MarceloAdan73 Date: Sat, 1 Aug 2026 18:28:22 -0300 Subject: [PATCH] fix: friendly single-line API error messages instead of raw JSON dumps Ollama (and other providers) return JSON error bodies which were dumped in full on every retry WARN, flooding the console with noisy output. - aiClient (x5): new formatApiError() extracts the friendly reason from JSON error bodies (error / error.message / message fields) and truncates non-JSON bodies to 200 chars. - Generators (x5): retry WARNs no longer repeat the full error message; the reason is shown once in the final [ERROR] line. - i18n leftovers: 'Resolviendo provider...' -> 'Resolving provider...' and 'truncado a' -> 'truncated to' now in English; tests updated. Verified: typecheck OK (6 workspaces), 626 tests passing, build OK, smoke test: Ollama 400 embedding model -> "Ollama API error 400: model nomic-embed-text:latest does not support chat". --- agent-code-review/index.ts | 2 +- agent-code-review/src/aiClient.ts | 30 ++++++++++++++++--- agent-code-review/src/reviewGenerator.ts | 7 ++--- .../tests/reviewGenerator.test.ts | 2 +- agent-doc-generator/index.ts | 4 +-- agent-doc-generator/src/aiClient.ts | 30 ++++++++++++++++--- agent-doc-generator/src/docGenerator.ts | 7 ++--- .../tests/docGenerator.test.ts | 2 +- agent-refactor/src/aiClient.ts | 30 ++++++++++++++++--- agent-refactor/src/refactorGenerator.ts | 7 ++--- .../tests/refactorGenerator.test.ts | 2 +- agent-security-audit/index.ts | 2 +- agent-security-audit/src/aiClient.ts | 30 ++++++++++++++++--- agent-security-audit/src/securityGenerator.ts | 7 ++--- .../tests/securityGenerator.test.ts | 2 +- agent-test-generator/index.ts | 2 +- agent-test-generator/src/aiClient.ts | 30 ++++++++++++++++--- agent-test-generator/src/testGenerator.ts | 7 ++--- .../tests/testGenerator.test.ts | 2 +- 19 files changed, 155 insertions(+), 50 deletions(-) diff --git a/agent-code-review/index.ts b/agent-code-review/index.ts index 5a84bd6..eeff764 100644 --- a/agent-code-review/index.ts +++ b/agent-code-review/index.ts @@ -182,7 +182,7 @@ async function main(): Promise { console.log('[DRY-RUN] Preview mode active - no files will be written\n'); } - const scanSpinner = ora('🔍 Resolviendo provider...').start(); + const scanSpinner = ora('🔍 Resolving provider...').start(); const resolved = await resolveProvider(); const apiKey = options.apiKey || getApiKeyForProvider(resolved.provider); diff --git a/agent-code-review/src/aiClient.ts b/agent-code-review/src/aiClient.ts index 1b2730e..cc63b7d 100644 --- a/agent-code-review/src/aiClient.ts +++ b/agent-code-review/src/aiClient.ts @@ -15,6 +15,28 @@ export interface CreateAIClientOptions { const PLACEHOLDER_KEY = 'your_gemini_api_key_here'; const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434'; +// Convierte el body de un error HTTP en una razon amigable de una sola linea. +// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON, +// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes. +function formatApiError(providerLabel: string, status: number, body: string): string { + try { + const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string }; + const reason = + typeof parsed.error === 'string' + ? parsed.error + : typeof parsed.error === 'object' && parsed.error?.message + ? parsed.error.message + : typeof parsed.message === 'string' + ? parsed.message + : undefined; + if (reason) return `${providerLabel} API error ${status}: ${reason}`; + } catch { + // body no es JSON, usar texto plano truncado + } + const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body; + return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`; +} + export class GeminiClient implements AIClient { private ai: GoogleGenAI; @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`OpenAI API error ${res.status}: ${body}`); + throw new Error(formatApiError('OpenAI', res.status, body)); } const data = (await res.json()) as { @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Anthropic API error ${res.status}: ${body}`); + throw new Error(formatApiError('Anthropic', res.status, body)); } const data = (await res.json()) as { @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`DeepSeek API error ${res.status}: ${body}`); + throw new Error(formatApiError('DeepSeek', res.status, body)); } const data = (await res.json()) as { @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Ollama API error ${res.status}: ${body}`); + throw new Error(formatApiError('Ollama', res.status, body)); } const data = (await res.json()) as { diff --git a/agent-code-review/src/reviewGenerator.ts b/agent-code-review/src/reviewGenerator.ts index f6eb296..43b5253 100644 --- a/agent-code-review/src/reviewGenerator.ts +++ b/agent-code-review/src/reviewGenerator.ts @@ -25,7 +25,7 @@ export async function generateReview( } if (content.length > maxChars) { - console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`); + console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`); } const trimmedContent = content.length > maxChars @@ -64,13 +64,12 @@ export async function generateReview( } if (attempt < MAX_RETRIES) { - console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`); + console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`); await sleep(RETRY_DELAY_MS); continue; } - console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`); - console.error(` ${error.message}`); + console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`); return null; } } diff --git a/agent-code-review/tests/reviewGenerator.test.ts b/agent-code-review/tests/reviewGenerator.test.ts index 74f8b84..7dea39c 100644 --- a/agent-code-review/tests/reviewGenerator.test.ts +++ b/agent-code-review/tests/reviewGenerator.test.ts @@ -230,7 +230,7 @@ describe('generateReview', () => { expect(warnSpy).toHaveBeenCalled(); const warnMsg = warnSpy.mock.calls[0]![0] as string; expect(warnMsg).toContain('long.ts'); - expect(warnMsg).toContain('truncado'); + expect(warnMsg).toContain('truncated to'); expect(warnMsg).toContain('20000'); warnSpy.mockRestore(); }); diff --git a/agent-doc-generator/index.ts b/agent-doc-generator/index.ts index fdbd0b3..a047830 100644 --- a/agent-doc-generator/index.ts +++ b/agent-doc-generator/index.ts @@ -124,7 +124,7 @@ async function main(): Promise { console.log('[DRY-RUN] Preview mode active - no files will be written\n'); } - const scanSpinner = ora('🔍 Resolviendo provider...').start(); + const scanSpinner = ora('🔍 Resolving provider...').start(); const resolved = await resolveProvider(); const apiKey = options.apiKey || getApiKeyForProvider(resolved.provider); @@ -287,7 +287,7 @@ async function main(): Promise { const docRelative = path.relative(projectRoot, docPath); writeSpinner.succeed(`Documentation generated: ${docRelative}`); } else { - writeSpinner.fail('Error al escribir DOCS.md'); + writeSpinner.fail('Error writing DOCS.md'); } } } diff --git a/agent-doc-generator/src/aiClient.ts b/agent-doc-generator/src/aiClient.ts index 1b2730e..cc63b7d 100644 --- a/agent-doc-generator/src/aiClient.ts +++ b/agent-doc-generator/src/aiClient.ts @@ -15,6 +15,28 @@ export interface CreateAIClientOptions { const PLACEHOLDER_KEY = 'your_gemini_api_key_here'; const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434'; +// Convierte el body de un error HTTP en una razon amigable de una sola linea. +// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON, +// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes. +function formatApiError(providerLabel: string, status: number, body: string): string { + try { + const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string }; + const reason = + typeof parsed.error === 'string' + ? parsed.error + : typeof parsed.error === 'object' && parsed.error?.message + ? parsed.error.message + : typeof parsed.message === 'string' + ? parsed.message + : undefined; + if (reason) return `${providerLabel} API error ${status}: ${reason}`; + } catch { + // body no es JSON, usar texto plano truncado + } + const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body; + return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`; +} + export class GeminiClient implements AIClient { private ai: GoogleGenAI; @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`OpenAI API error ${res.status}: ${body}`); + throw new Error(formatApiError('OpenAI', res.status, body)); } const data = (await res.json()) as { @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Anthropic API error ${res.status}: ${body}`); + throw new Error(formatApiError('Anthropic', res.status, body)); } const data = (await res.json()) as { @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`DeepSeek API error ${res.status}: ${body}`); + throw new Error(formatApiError('DeepSeek', res.status, body)); } const data = (await res.json()) as { @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Ollama API error ${res.status}: ${body}`); + throw new Error(formatApiError('Ollama', res.status, body)); } const data = (await res.json()) as { diff --git a/agent-doc-generator/src/docGenerator.ts b/agent-doc-generator/src/docGenerator.ts index 3666cd9..43ee009 100644 --- a/agent-doc-generator/src/docGenerator.ts +++ b/agent-doc-generator/src/docGenerator.ts @@ -24,7 +24,7 @@ export async function generateDocumentation( } if (content.length > maxChars) { - console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`); + console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`); } const trimmedContent = content.length > maxChars @@ -62,13 +62,12 @@ export async function generateDocumentation( } if (attempt < MAX_RETRIES) { - console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`); + console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`); await sleep(RETRY_DELAY_MS); continue; } - console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`); - console.error(` ${error.message}`); + console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`); return null; } } diff --git a/agent-doc-generator/tests/docGenerator.test.ts b/agent-doc-generator/tests/docGenerator.test.ts index c32a7ef..b563f01 100644 --- a/agent-doc-generator/tests/docGenerator.test.ts +++ b/agent-doc-generator/tests/docGenerator.test.ts @@ -229,7 +229,7 @@ describe('generateDocumentation', () => { expect(warnSpy).toHaveBeenCalled(); const warnMsg = warnSpy.mock.calls[0]![0] as string; expect(warnMsg).toContain('long.ts'); - expect(warnMsg).toContain('truncado'); + expect(warnMsg).toContain('truncated to'); expect(warnMsg).toContain('20000'); warnSpy.mockRestore(); }); diff --git a/agent-refactor/src/aiClient.ts b/agent-refactor/src/aiClient.ts index 1b2730e..cc63b7d 100644 --- a/agent-refactor/src/aiClient.ts +++ b/agent-refactor/src/aiClient.ts @@ -15,6 +15,28 @@ export interface CreateAIClientOptions { const PLACEHOLDER_KEY = 'your_gemini_api_key_here'; const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434'; +// Convierte el body de un error HTTP en una razon amigable de una sola linea. +// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON, +// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes. +function formatApiError(providerLabel: string, status: number, body: string): string { + try { + const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string }; + const reason = + typeof parsed.error === 'string' + ? parsed.error + : typeof parsed.error === 'object' && parsed.error?.message + ? parsed.error.message + : typeof parsed.message === 'string' + ? parsed.message + : undefined; + if (reason) return `${providerLabel} API error ${status}: ${reason}`; + } catch { + // body no es JSON, usar texto plano truncado + } + const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body; + return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`; +} + export class GeminiClient implements AIClient { private ai: GoogleGenAI; @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`OpenAI API error ${res.status}: ${body}`); + throw new Error(formatApiError('OpenAI', res.status, body)); } const data = (await res.json()) as { @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Anthropic API error ${res.status}: ${body}`); + throw new Error(formatApiError('Anthropic', res.status, body)); } const data = (await res.json()) as { @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`DeepSeek API error ${res.status}: ${body}`); + throw new Error(formatApiError('DeepSeek', res.status, body)); } const data = (await res.json()) as { @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Ollama API error ${res.status}: ${body}`); + throw new Error(formatApiError('Ollama', res.status, body)); } const data = (await res.json()) as { diff --git a/agent-refactor/src/refactorGenerator.ts b/agent-refactor/src/refactorGenerator.ts index 34fce7e..901a634 100644 --- a/agent-refactor/src/refactorGenerator.ts +++ b/agent-refactor/src/refactorGenerator.ts @@ -25,7 +25,7 @@ export async function generateRefactor( } if (content.length > maxChars) { - console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`); + console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`); } const trimmedContent = content.length > maxChars @@ -64,13 +64,12 @@ export async function generateRefactor( } if (attempt < MAX_RETRIES) { - console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`); + console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`); await sleep(RETRY_DELAY_MS); continue; } - console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`); - console.error(` ${error.message}`); + console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`); return null; } } diff --git a/agent-refactor/tests/refactorGenerator.test.ts b/agent-refactor/tests/refactorGenerator.test.ts index 2c536c6..a904c08 100644 --- a/agent-refactor/tests/refactorGenerator.test.ts +++ b/agent-refactor/tests/refactorGenerator.test.ts @@ -230,7 +230,7 @@ describe('generateRefactor', () => { expect(warnSpy).toHaveBeenCalled(); const warnMsg = warnSpy.mock.calls[0]![0] as string; expect(warnMsg).toContain('long.ts'); - expect(warnMsg).toContain('truncado'); + expect(warnMsg).toContain('truncated to'); expect(warnMsg).toContain('20000'); warnSpy.mockRestore(); }); diff --git a/agent-security-audit/index.ts b/agent-security-audit/index.ts index 0ccad59..87ed412 100644 --- a/agent-security-audit/index.ts +++ b/agent-security-audit/index.ts @@ -189,7 +189,7 @@ async function main(): Promise { console.log('[DRY-RUN] Preview mode active - no files will be written\n'); } - const scanSpinner = ora('🔍 Resolviendo provider...').start(); + const scanSpinner = ora('🔍 Resolving provider...').start(); const resolved = await resolveProvider(); const apiKey = options.apiKey || getApiKeyForProvider(resolved.provider); diff --git a/agent-security-audit/src/aiClient.ts b/agent-security-audit/src/aiClient.ts index 1b2730e..cc63b7d 100644 --- a/agent-security-audit/src/aiClient.ts +++ b/agent-security-audit/src/aiClient.ts @@ -15,6 +15,28 @@ export interface CreateAIClientOptions { const PLACEHOLDER_KEY = 'your_gemini_api_key_here'; const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434'; +// Convierte el body de un error HTTP en una razon amigable de una sola linea. +// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON, +// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes. +function formatApiError(providerLabel: string, status: number, body: string): string { + try { + const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string }; + const reason = + typeof parsed.error === 'string' + ? parsed.error + : typeof parsed.error === 'object' && parsed.error?.message + ? parsed.error.message + : typeof parsed.message === 'string' + ? parsed.message + : undefined; + if (reason) return `${providerLabel} API error ${status}: ${reason}`; + } catch { + // body no es JSON, usar texto plano truncado + } + const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body; + return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`; +} + export class GeminiClient implements AIClient { private ai: GoogleGenAI; @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`OpenAI API error ${res.status}: ${body}`); + throw new Error(formatApiError('OpenAI', res.status, body)); } const data = (await res.json()) as { @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Anthropic API error ${res.status}: ${body}`); + throw new Error(formatApiError('Anthropic', res.status, body)); } const data = (await res.json()) as { @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`DeepSeek API error ${res.status}: ${body}`); + throw new Error(formatApiError('DeepSeek', res.status, body)); } const data = (await res.json()) as { @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Ollama API error ${res.status}: ${body}`); + throw new Error(formatApiError('Ollama', res.status, body)); } const data = (await res.json()) as { diff --git a/agent-security-audit/src/securityGenerator.ts b/agent-security-audit/src/securityGenerator.ts index 78f23d1..97129f5 100644 --- a/agent-security-audit/src/securityGenerator.ts +++ b/agent-security-audit/src/securityGenerator.ts @@ -25,7 +25,7 @@ export async function generateAudit( } if (content.length > maxChars) { - console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`); + console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`); } const trimmedContent = content.length > maxChars @@ -64,13 +64,12 @@ export async function generateAudit( } if (attempt < MAX_RETRIES) { - console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`); + console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`); await sleep(RETRY_DELAY_MS); continue; } - console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`); - console.error(` ${error.message}`); + console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`); return null; } } diff --git a/agent-security-audit/tests/securityGenerator.test.ts b/agent-security-audit/tests/securityGenerator.test.ts index d501ed8..31331ef 100644 --- a/agent-security-audit/tests/securityGenerator.test.ts +++ b/agent-security-audit/tests/securityGenerator.test.ts @@ -230,7 +230,7 @@ describe('generateAudit', () => { expect(warnSpy).toHaveBeenCalled(); const warnMsg = warnSpy.mock.calls[0]![0] as string; expect(warnMsg).toContain('long.ts'); - expect(warnMsg).toContain('truncado'); + expect(warnMsg).toContain('truncated to'); expect(warnMsg).toContain('20000'); warnSpy.mockRestore(); }); diff --git a/agent-test-generator/index.ts b/agent-test-generator/index.ts index bf335ac..1557551 100644 --- a/agent-test-generator/index.ts +++ b/agent-test-generator/index.ts @@ -124,7 +124,7 @@ async function main(): Promise { console.log('[DRY-RUN] Preview mode active - no files will be written\n'); } - const scanSpinner = ora('🔍 Resolviendo provider...').start(); + const scanSpinner = ora('🔍 Resolving provider...').start(); const resolved = await resolveProvider(); const apiKey = options.apiKey || getApiKeyForProvider(resolved.provider); diff --git a/agent-test-generator/src/aiClient.ts b/agent-test-generator/src/aiClient.ts index 1b2730e..cc63b7d 100644 --- a/agent-test-generator/src/aiClient.ts +++ b/agent-test-generator/src/aiClient.ts @@ -15,6 +15,28 @@ export interface CreateAIClientOptions { const PLACEHOLDER_KEY = 'your_gemini_api_key_here'; const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434'; +// Convierte el body de un error HTTP en una razon amigable de una sola linea. +// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON, +// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes. +function formatApiError(providerLabel: string, status: number, body: string): string { + try { + const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string }; + const reason = + typeof parsed.error === 'string' + ? parsed.error + : typeof parsed.error === 'object' && parsed.error?.message + ? parsed.error.message + : typeof parsed.message === 'string' + ? parsed.message + : undefined; + if (reason) return `${providerLabel} API error ${status}: ${reason}`; + } catch { + // body no es JSON, usar texto plano truncado + } + const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body; + return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`; +} + export class GeminiClient implements AIClient { private ai: GoogleGenAI; @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`OpenAI API error ${res.status}: ${body}`); + throw new Error(formatApiError('OpenAI', res.status, body)); } const data = (await res.json()) as { @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Anthropic API error ${res.status}: ${body}`); + throw new Error(formatApiError('Anthropic', res.status, body)); } const data = (await res.json()) as { @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`DeepSeek API error ${res.status}: ${body}`); + throw new Error(formatApiError('DeepSeek', res.status, body)); } const data = (await res.json()) as { @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient { if (!res.ok) { const body = await res.text().catch(() => ''); - throw new Error(`Ollama API error ${res.status}: ${body}`); + throw new Error(formatApiError('Ollama', res.status, body)); } const data = (await res.json()) as { diff --git a/agent-test-generator/src/testGenerator.ts b/agent-test-generator/src/testGenerator.ts index 35600c1..a30277c 100644 --- a/agent-test-generator/src/testGenerator.ts +++ b/agent-test-generator/src/testGenerator.ts @@ -24,7 +24,7 @@ export async function generateTests( } if (content.length > maxChars) { - console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`); + console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`); } const trimmedContent = content.length > maxChars @@ -62,13 +62,12 @@ export async function generateTests( } if (attempt < MAX_RETRIES) { - console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`); + console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`); await sleep(RETRY_DELAY_MS); continue; } - console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`); - console.error(` ${error.message}`); + console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`); return null; } } diff --git a/agent-test-generator/tests/testGenerator.test.ts b/agent-test-generator/tests/testGenerator.test.ts index dc4c3f9..dca35d3 100644 --- a/agent-test-generator/tests/testGenerator.test.ts +++ b/agent-test-generator/tests/testGenerator.test.ts @@ -229,7 +229,7 @@ describe('generateTests', () => { expect(warnSpy).toHaveBeenCalled(); const warnMsg = warnSpy.mock.calls[0]![0] as string; expect(warnMsg).toContain('long.ts'); - expect(warnMsg).toContain('truncado'); + expect(warnMsg).toContain('truncated to'); expect(warnMsg).toContain('20000'); warnSpy.mockRestore(); });