Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 73 additions & 11 deletions src/services/aiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ ${options.user}` : options.user;
system,
user: prompt,
temperature: 0.3,
maxTokens: 400,
maxTokens: 700,
});

return this.parseAIResponse(content);
Expand Down Expand Up @@ -351,20 +351,25 @@ Focus on practicality and accurate categorization to help users quickly understa

private parseAIResponse(content: string): { summary: string; tags: string[]; platforms: string[] } {
try {
// Try to extract JSON from the response
const jsonMatch = content.match(/\{[\s\S]*\}/);
if (jsonMatch) {
const parsed = JSON.parse(jsonMatch[0]);
const cleaned = content
.trim()
.replace(/^```(?:json)?\s*/i, '')
.replace(/\s*```$/i, '')
.trim();

const parsed = this.extractAndParseAIJson(cleaned);
if (parsed) {
return {
summary: parsed.summary || (this.language === 'zh' ? '无法生成概述' : 'Unable to generate summary'),
tags: Array.isArray(parsed.tags) ? parsed.tags.slice(0, 5) : [],
platforms: Array.isArray(parsed.platforms) ? parsed.platforms.slice(0, 8) : [],
summary: typeof parsed.summary === 'string' && parsed.summary.trim()
? parsed.summary.trim()
: (this.language === 'zh' ? '无法生成概述' : 'Unable to generate summary'),
tags: Array.isArray(parsed.tags) ? parsed.tags.filter((v) => typeof v === 'string').slice(0, 5) : [],
platforms: Array.isArray(parsed.platforms) ? parsed.platforms.filter((v) => typeof v === 'string').slice(0, 8) : [],
};
}

// Fallback parsing

return {
summary: content.substring(0, 50) + '...',
summary: cleaned.substring(0, 50) + (cleaned.length > 50 ? '...' : ''),
tags: [],
platforms: [],
};
Comment on lines +370 to 375
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Empty content fallback yields an empty summary string.

If cleaned is empty (e.g., the AI returned only code fences with no content), cleaned.substring(0, 50) returns "". This is inconsistent with the other fallback paths that return explicit messages like "Unable to generate summary" (line 365) or "Analysis failed" (line 379).

🔧 Proposed fix for consistency
      return {
-       summary: cleaned.substring(0, 50) + (cleaned.length > 50 ? '...' : ''),
+       summary: cleaned.trim()
+         ? cleaned.substring(0, 50) + (cleaned.length > 50 ? '...' : '')
+         : (this.language === 'zh' ? '无法生成概述' : 'Unable to generate summary'),
        tags: [],
        platforms: [],
      };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
summary: content.substring(0, 50) + '...',
summary: cleaned.substring(0, 50) + (cleaned.length > 50 ? '...' : ''),
tags: [],
platforms: [],
};
return {
summary: cleaned.trim()
? cleaned.substring(0, 50) + (cleaned.length > 50 ? '...' : '')
: (this.language === 'zh' ? '无法生成概述' : 'Unable to generate summary'),
tags: [],
platforms: [],
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/aiService.ts` around lines 370 - 375, The current return uses
cleaned.substring(0, 50) which yields an empty string when cleaned is empty;
update the summary fallback so that if cleaned is falsy or trimmed length is 0
it returns a clear message like "Unable to generate summary" (or "Analysis
failed" depending on context) instead of an empty string—modify the return logic
where cleaned is used (the block that builds { summary: ..., tags: [],
platforms: [] }) to check cleaned.trim().length and choose the explicit fallback
message, otherwise keep the existing substring(0, 50) + ellipsis behavior.

Expand All @@ -378,6 +383,63 @@ Focus on practicality and accurate categorization to help users quickly understa
}
}

private extractAndParseAIJson(content: string): Record<string, unknown> | null {
const direct = this.tryParseJsonObject(content);
if (direct) return direct;

const start = content.indexOf('{');
if (start === -1) return null;

let inString = false;
let escaped = false;
let depth = 0;

for (let i = start; i < content.length; i++) {
const char = content[i];

if (escaped) {
escaped = false;
continue;
}

if (char === '\\') {
escaped = true;
continue;
}

if (char === '"') {
inString = !inString;
continue;
}

if (inString) continue;

if (char === '{') depth++;
if (char === '}') {
depth--;
if (depth === 0) {
return this.tryParseJsonObject(content.slice(start, i + 1));
}
}
}

return null;
}

private tryParseJsonObject(text: string): Record<string, unknown> | null {
const trimmed = text.trim();
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) return null;

try {
const parsed = JSON.parse(trimmed);
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
? (parsed as Record<string, unknown>)
: null;
} catch {
return null;
}
}

private fallbackAnalysis(repository: Repository): { summary: string; tags: string[]; platforms: string[] } {
const summary = repository.description
? `${repository.description}(${repository.language || (this.language === 'zh' ? '未知语言' : 'Unknown language')}${this.language === 'zh' ? '项目' : ' project'})`
Expand Down
Loading