autosnippet 3.0.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -324
- package/bin/api-server.js +1 -1
- package/bin/cli.js +204 -244
- package/bin/mcp-server.js +5 -3
- package/config/knowledge-base.config.js +132 -132
- package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
- package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
- package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/bootstrap.js +8 -8
- package/lib/cli/AiScanService.js +86 -40
- package/lib/cli/KnowledgeSyncService.js +113 -74
- package/lib/cli/SetupService.js +439 -277
- package/lib/cli/UpgradeService.js +63 -100
- package/lib/core/AstAnalyzer.js +276 -597
- package/lib/core/ast/ProjectGraph.js +101 -40
- package/lib/core/ast/ensure-grammars.js +232 -0
- package/lib/core/ast/index.js +115 -0
- package/lib/core/ast/lang-dart.js +661 -0
- package/lib/core/ast/lang-go.js +530 -0
- package/lib/core/ast/lang-java.js +435 -0
- package/lib/core/ast/lang-javascript.js +272 -0
- package/lib/core/ast/lang-kotlin.js +423 -0
- package/lib/core/ast/lang-objc.js +388 -0
- package/lib/core/ast/lang-python.js +371 -0
- package/lib/core/ast/lang-swift.js +337 -0
- package/lib/core/ast/lang-typescript.js +503 -0
- package/lib/core/capability/CapabilityProbe.js +18 -9
- package/lib/core/constitution/Constitution.js +2 -3
- package/lib/core/constitution/ConstitutionValidator.js +65 -24
- package/lib/core/discovery/DartDiscoverer.js +534 -0
- package/lib/core/discovery/DiscovererRegistry.js +83 -0
- package/lib/core/discovery/GenericDiscoverer.js +225 -0
- package/lib/core/discovery/GoDiscoverer.js +541 -0
- package/lib/core/discovery/JvmDiscoverer.js +506 -0
- package/lib/core/discovery/NodeDiscoverer.js +466 -0
- package/lib/core/discovery/ProjectDiscoverer.js +93 -0
- package/lib/core/discovery/PythonDiscoverer.js +338 -0
- package/lib/core/discovery/SpmDiscoverer.js +5 -0
- package/lib/core/discovery/index.js +53 -0
- package/lib/core/enhancement/EnhancementPack.js +71 -0
- package/lib/core/enhancement/EnhancementRegistry.js +47 -0
- package/lib/core/enhancement/android-enhancement.js +102 -0
- package/lib/core/enhancement/django-enhancement.js +70 -0
- package/lib/core/enhancement/fastapi-enhancement.js +63 -0
- package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
- package/lib/core/enhancement/go-web-enhancement.js +201 -0
- package/lib/core/enhancement/index.js +65 -0
- package/lib/core/enhancement/node-server-enhancement.js +88 -0
- package/lib/core/enhancement/react-enhancement.js +86 -0
- package/lib/core/enhancement/spring-enhancement.js +112 -0
- package/lib/core/enhancement/vue-enhancement.js +96 -0
- package/lib/core/gateway/Gateway.js +8 -9
- package/lib/core/gateway/GatewayActionRegistry.js +1 -1
- package/lib/core/permission/PermissionManager.js +12 -8
- package/lib/domain/index.js +13 -9
- package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
- package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
- package/lib/domain/knowledge/Lifecycle.js +22 -22
- package/lib/domain/knowledge/index.js +9 -12
- package/lib/domain/knowledge/values/Constraints.js +31 -21
- package/lib/domain/knowledge/values/Content.js +21 -13
- package/lib/domain/knowledge/values/Quality.js +31 -18
- package/lib/domain/knowledge/values/Reasoning.js +20 -12
- package/lib/domain/knowledge/values/Relations.js +37 -25
- package/lib/domain/knowledge/values/Stats.js +18 -12
- package/lib/domain/knowledge/values/index.js +4 -3
- package/lib/domain/snippet/Snippet.js +35 -10
- package/lib/external/ai/AiFactory.js +48 -16
- package/lib/external/ai/AiProvider.js +184 -90
- package/lib/external/ai/providers/ClaudeProvider.js +25 -12
- package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
- package/lib/external/ai/providers/MockProvider.js +9 -3
- package/lib/external/ai/providers/OpenAiProvider.js +51 -29
- package/lib/external/mcp/McpServer.js +66 -36
- package/lib/external/mcp/errorHandler.js +23 -11
- package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
- package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
- package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
- package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
- package/lib/external/mcp/handlers/bootstrap.js +691 -168
- package/lib/external/mcp/handlers/browse.js +66 -22
- package/lib/external/mcp/handlers/candidate.js +118 -35
- package/lib/external/mcp/handlers/consolidated.js +49 -17
- package/lib/external/mcp/handlers/guard.js +104 -39
- package/lib/external/mcp/handlers/knowledge.js +60 -36
- package/lib/external/mcp/handlers/search.js +43 -14
- package/lib/external/mcp/handlers/skill.js +120 -45
- package/lib/external/mcp/handlers/structure.js +240 -86
- package/lib/external/mcp/handlers/system.js +42 -12
- package/lib/external/mcp/handlers/wiki.js +58 -33
- package/lib/external/mcp/tools.js +306 -123
- package/lib/http/HttpServer.js +72 -47
- package/lib/http/middleware/RateLimiter.js +5 -3
- package/lib/http/middleware/errorHandler.js +6 -1
- package/lib/http/middleware/requestLogger.js +14 -3
- package/lib/http/middleware/roleResolver.js +30 -23
- package/lib/http/routes/ai.js +387 -265
- package/lib/http/routes/auth.js +81 -61
- package/lib/http/routes/candidates.js +430 -320
- package/lib/http/routes/commands.js +289 -189
- package/lib/http/routes/extract.js +158 -125
- package/lib/http/routes/guardRules.js +309 -217
- package/lib/http/routes/knowledge.js +213 -154
- package/lib/http/routes/modules.js +578 -0
- package/lib/http/routes/monitoring.js +6 -6
- package/lib/http/routes/recipes.js +104 -93
- package/lib/http/routes/search.js +361 -305
- package/lib/http/routes/skills.js +145 -98
- package/lib/http/routes/snippets.js +42 -30
- package/lib/http/routes/spm.js +3 -405
- package/lib/http/routes/violations.js +113 -93
- package/lib/http/routes/wiki.js +211 -170
- package/lib/http/utils/routeHelpers.js +3 -1
- package/lib/http/utils/sse-sessions.js +16 -6
- package/lib/http/utils/sse.js +15 -5
- package/lib/infrastructure/audit/AuditLogger.js +5 -2
- package/lib/infrastructure/audit/AuditStore.js +10 -7
- package/lib/infrastructure/cache/CacheService.js +3 -1
- package/lib/infrastructure/cache/GraphCache.js +8 -4
- package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
- package/lib/infrastructure/config/ConfigLoader.js +9 -5
- package/lib/infrastructure/config/Defaults.js +30 -10
- package/lib/infrastructure/config/Paths.js +28 -8
- package/lib/infrastructure/config/TriggerSymbol.js +22 -10
- package/lib/infrastructure/database/DatabaseConnection.js +15 -10
- package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
- package/lib/infrastructure/external/ClipboardManager.js +6 -2
- package/lib/infrastructure/external/NativeUi.js +50 -43
- package/lib/infrastructure/external/OpenBrowser.js +14 -17
- package/lib/infrastructure/external/XcodeAutomation.js +14 -258
- package/lib/infrastructure/logging/Logger.js +46 -30
- package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
- package/lib/infrastructure/paths/HeaderResolver.js +25 -9
- package/lib/infrastructure/paths/PathFinder.js +34 -12
- package/lib/infrastructure/plugin/PluginManager.js +26 -8
- package/lib/infrastructure/realtime/RealtimeService.js +2 -2
- package/lib/infrastructure/vector/Chunker.js +22 -7
- package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
- package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
- package/lib/infrastructure/vector/VectorStore.js +28 -10
- package/lib/injection/ServiceContainer.js +247 -93
- package/lib/platform/ios/index.js +63 -0
- package/lib/platform/ios/routes/spm.js +437 -0
- package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
- package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
- package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
- package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
- package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
- package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
- package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
- package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
- package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
- package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
- package/lib/repository/base/BaseRepository.js +7 -9
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
- package/lib/repository/token/TokenUsageStore.js +4 -2
- package/lib/service/automation/ActionPipeline.js +1 -1
- package/lib/service/automation/AutomationOrchestrator.js +8 -4
- package/lib/service/automation/ContextCollector.js +7 -5
- package/lib/service/automation/DirectiveDetector.js +23 -16
- package/lib/service/automation/FileWatcher.js +112 -56
- package/lib/service/automation/TriggerResolver.js +6 -4
- package/lib/service/automation/handlers/AlinkHandler.js +24 -12
- package/lib/service/automation/handlers/CreateHandler.js +19 -20
- package/lib/service/automation/handlers/DraftHandler.js +14 -8
- package/lib/service/automation/handlers/GuardHandler.js +93 -63
- package/lib/service/automation/handlers/HeaderHandler.js +1 -6
- package/lib/service/automation/handlers/SearchHandler.js +155 -88
- package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
- package/lib/service/candidate/SimilarityService.js +25 -9
- package/lib/service/chat/AnalystAgent.js +50 -24
- package/lib/service/chat/CandidateGuardrail.js +143 -17
- package/lib/service/chat/ChatAgent.js +655 -260
- package/lib/service/chat/ContextWindow.js +116 -71
- package/lib/service/chat/ConversationStore.js +77 -36
- package/lib/service/chat/EpisodicConsolidator.js +47 -23
- package/lib/service/chat/HandoffProtocol.js +98 -22
- package/lib/service/chat/Memory.js +34 -14
- package/lib/service/chat/ProducerAgent.js +40 -20
- package/lib/service/chat/ProjectSemanticMemory.js +109 -78
- package/lib/service/chat/ReasoningLayer.js +148 -70
- package/lib/service/chat/ReasoningTrace.js +44 -32
- package/lib/service/chat/TaskPipeline.js +39 -19
- package/lib/service/chat/ToolRegistry.js +48 -29
- package/lib/service/chat/WorkingMemory.js +44 -18
- package/lib/service/chat/tools.js +1096 -494
- package/lib/service/context/RecipeExtractor.js +132 -51
- package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
- package/lib/service/cursor/KnowledgeCompressor.js +25 -22
- package/lib/service/cursor/RulesGenerator.js +13 -7
- package/lib/service/cursor/SkillsSyncer.js +77 -27
- package/lib/service/cursor/TokenBudget.js +2 -2
- package/lib/service/cursor/TopicClassifier.js +54 -20
- package/lib/service/guard/ComplianceReporter.js +55 -43
- package/lib/service/guard/ExclusionManager.js +67 -29
- package/lib/service/guard/GuardCheckEngine.js +381 -86
- package/lib/service/guard/GuardFeedbackLoop.js +22 -10
- package/lib/service/guard/GuardService.js +29 -19
- package/lib/service/guard/RuleLearner.js +55 -23
- package/lib/service/guard/SourceFileCollector.js +27 -20
- package/lib/service/guard/ViolationsStore.js +43 -38
- package/lib/service/knowledge/CodeEntityGraph.js +147 -82
- package/lib/service/knowledge/ConfidenceRouter.js +12 -10
- package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
- package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
- package/lib/service/knowledge/KnowledgeService.js +222 -112
- package/lib/service/module/ModuleService.js +969 -0
- package/lib/service/quality/FeedbackCollector.js +27 -15
- package/lib/service/quality/QualityScorer.js +78 -24
- package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
- package/lib/service/recipe/RecipeParser.js +78 -45
- package/lib/service/search/CoarseRanker.js +43 -28
- package/lib/service/search/CrossEncoderReranker.js +32 -21
- package/lib/service/search/InvertedIndex.js +21 -7
- package/lib/service/search/MultiSignalRanker.js +90 -28
- package/lib/service/search/RetrievalFunnel.js +45 -24
- package/lib/service/search/SearchEngine.js +255 -103
- package/lib/service/skills/EventAggregator.js +32 -15
- package/lib/service/skills/SignalCollector.js +140 -64
- package/lib/service/skills/SkillAdvisor.js +79 -42
- package/lib/service/skills/SkillHooks.js +16 -14
- package/lib/service/snippet/PlaceholderConverter.js +5 -0
- package/lib/service/snippet/SnippetFactory.js +116 -99
- package/lib/service/snippet/SnippetInstaller.js +234 -62
- package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
- package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
- package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
- package/lib/service/wiki/WikiGenerator.js +637 -263
- package/lib/shared/DimensionCopyRegistry.js +472 -0
- package/lib/shared/LanguageService.js +399 -0
- package/lib/shared/PathGuard.js +45 -28
- package/lib/shared/RecipeReadinessChecker.js +72 -12
- package/lib/shared/constants.js +41 -41
- package/lib/shared/errors/BaseError.js +2 -2
- package/lib/shared/errors/index.js +4 -4
- package/lib/shared/similarity.js +25 -8
- package/lib/shared/token-utils.js +6 -2
- package/lib/shared/utils/common.js +12 -4
- package/package.json +49 -13
- package/scripts/bench-real-projects.mjs +256 -0
- package/scripts/build-native-ui.js +30 -30
- package/scripts/clear-old-vector-index.js +5 -35
- package/scripts/clear-vector-cache.js +7 -37
- package/scripts/collect-test-project-stats.mjs +160 -0
- package/scripts/diagnose-mcp.js +41 -32
- package/scripts/ensure-parse-package.js +6 -9
- package/scripts/generate-recipe-drafts.js +116 -77
- package/scripts/init-db.js +3 -20
- package/scripts/init-snippets.js +305 -0
- package/scripts/init-vector-db.js +173 -170
- package/scripts/install-cursor-skill.js +148 -104
- package/scripts/install-full.js +8 -21
- package/scripts/install-vscode-copilot.js +146 -145
- package/scripts/migrate-md-to-knowledge.mjs +139 -151
- package/scripts/postinstall-safe.js +5 -17
- package/scripts/recipe-audit.js +106 -82
- package/scripts/release.js +283 -323
- package/scripts/setup-mcp-config.js +60 -52
- package/scripts/verify-context-api.js +20 -20
- package/skills/autosnippet-analysis/SKILL.md +10 -6
- package/skills/autosnippet-candidates/SKILL.md +27 -26
- package/skills/autosnippet-coldstart/SKILL.md +555 -38
- package/skills/autosnippet-concepts/SKILL.md +349 -337
- package/skills/autosnippet-create/SKILL.md +5 -5
- package/skills/autosnippet-reference-dart/SKILL.md +543 -0
- package/skills/autosnippet-reference-go/SKILL.md +539 -0
- package/skills/autosnippet-reference-java/SKILL.md +534 -0
- package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
- package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
- package/skills/autosnippet-reference-objc/SKILL.md +29 -6
- package/skills/autosnippet-reference-python/SKILL.md +800 -0
- package/skills/autosnippet-reference-swift/SKILL.md +70 -14
- package/skills/autosnippet-structure/SKILL.md +4 -4
- package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
- package/templates/recipes-setup/README.md +2 -2
- package/templates/recipes-setup/_template.md +1 -1
- package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
- package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
- package/resources/asd-entry/main.swift +0 -159
- package/scripts/build-asd-entry.js +0 -51
- package/scripts/init-xcode-snippets.js +0 -311
- package/template.json +0 -39
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* 所有具体 Provider 必须实现这3个方法
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { LanguageService } from '../../shared/LanguageService.js';
|
|
7
|
+
|
|
6
8
|
export class AiProvider {
|
|
7
9
|
constructor(config = {}) {
|
|
8
10
|
this.model = config.model || '';
|
|
@@ -13,11 +15,11 @@ export class AiProvider {
|
|
|
13
15
|
this.name = 'abstract';
|
|
14
16
|
|
|
15
17
|
// ── CircuitBreaker 状态 ──
|
|
16
|
-
this._circuitState = 'CLOSED';
|
|
17
|
-
this._circuitFailures = 0;
|
|
18
|
-
this._circuitThreshold = config.circuitThreshold || 5;
|
|
19
|
-
this._circuitOpenedAt = 0;
|
|
20
|
-
this._circuitCooldownMs = 30_000;
|
|
18
|
+
this._circuitState = 'CLOSED'; // 'CLOSED' | 'OPEN' | 'HALF_OPEN'
|
|
19
|
+
this._circuitFailures = 0; // 连续失败计数
|
|
20
|
+
this._circuitThreshold = config.circuitThreshold || 5; // 触发熔断的连续失败次数
|
|
21
|
+
this._circuitOpenedAt = 0; // 熔断打开时间
|
|
22
|
+
this._circuitCooldownMs = 30_000; // 初始冷却 30 秒
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -102,8 +104,11 @@ export class AiProvider {
|
|
|
102
104
|
// 默认降级: 忽略 tools/toolChoice,走纯文本 chat()
|
|
103
105
|
const messages = opts.messages || [];
|
|
104
106
|
const history = messages
|
|
105
|
-
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
106
|
-
.map(m => ({
|
|
107
|
+
.filter((m) => m.role === 'user' || m.role === 'assistant')
|
|
108
|
+
.map((m) => ({
|
|
109
|
+
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
110
|
+
content: m.content || '',
|
|
111
|
+
}));
|
|
107
112
|
const text = await this.chat(prompt, {
|
|
108
113
|
history,
|
|
109
114
|
systemPrompt: opts.systemPrompt,
|
|
@@ -137,7 +142,9 @@ export class AiProvider {
|
|
|
137
142
|
maxTokens: opts.maxTokens ?? 32768,
|
|
138
143
|
systemPrompt: opts.systemPrompt,
|
|
139
144
|
});
|
|
140
|
-
if (!response || response.trim().length === 0)
|
|
145
|
+
if (!response || response.trim().length === 0) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
141
148
|
const openChar = opts.openChar || '{';
|
|
142
149
|
const closeChar = opts.closeChar || '}';
|
|
143
150
|
return this.extractJSON(response, openChar, closeChar);
|
|
@@ -161,7 +168,10 @@ export class AiProvider {
|
|
|
161
168
|
maxTokens: 32768,
|
|
162
169
|
});
|
|
163
170
|
if (!Array.isArray(parsed)) {
|
|
164
|
-
this._log(
|
|
171
|
+
this._log(
|
|
172
|
+
'warn',
|
|
173
|
+
`[extractRecipes] structured output parse failed for target: ${targetName}`
|
|
174
|
+
);
|
|
165
175
|
return [];
|
|
166
176
|
}
|
|
167
177
|
if (parsed.length === 0) {
|
|
@@ -178,16 +188,17 @@ export class AiProvider {
|
|
|
178
188
|
if (this.logger && typeof this.logger[level] === 'function') {
|
|
179
189
|
this.logger[level](message);
|
|
180
190
|
} else {
|
|
181
|
-
console[level === 'warn' ? 'warn' : 'log'](message);
|
|
182
191
|
}
|
|
183
|
-
} catch {
|
|
192
|
+
} catch {
|
|
193
|
+
/* best effort */
|
|
194
|
+
}
|
|
184
195
|
}
|
|
185
196
|
|
|
186
197
|
/**
|
|
187
198
|
* 构建 extractRecipes 标准提示词(语言自适应 + Skill 增强)
|
|
188
199
|
*/
|
|
189
200
|
_buildExtractPrompt(targetName, filesContent, options = {}) {
|
|
190
|
-
const files = filesContent.map(f => `--- FILE: ${f.name} ---\n${f.content}`).join('\n\n');
|
|
201
|
+
const files = filesContent.map((f) => `--- FILE: ${f.name} ---\n${f.content}`).join('\n\n');
|
|
191
202
|
|
|
192
203
|
// 检测文件主要语言
|
|
193
204
|
const langProfile = this._detectLanguageProfile(filesContent);
|
|
@@ -251,7 +262,7 @@ Each item MUST use the following V3 KnowledgeEntry structure:
|
|
|
251
262
|
- dontClause (string): What NOT to do (e.g. "Don't instantiate services with new directly")
|
|
252
263
|
- whenClause (string): When this pattern applies (e.g. "When creating a new ViewController subclass")
|
|
253
264
|
- topicHint (string): Group label for related patterns (e.g. "Networking", "UI-Layout", "Error-Handling")
|
|
254
|
-
- coreCode (string): Minimal 3-10 line code skeleton that captures the essence
|
|
265
|
+
- coreCode (string): Minimal 3-10 line code skeleton that captures the essence. Must be syntactically complete — balanced brackets/parentheses, never start with } or ) and never end with { or (
|
|
255
266
|
- constraints (object, optional): { "preconditions": ["<conditions that must be true before using this pattern>"], "sideEffects": ["<observable side effects of using this code>"], "boundaries": ["<usage limitations or scope restrictions>"] }
|
|
256
267
|
- aiInsight (string, optional): One-sentence concise insight — the single most important takeaway about this code pattern
|
|
257
268
|
|
|
@@ -312,7 +323,7 @@ Each item MUST use the following V3 KnowledgeEntry structure:
|
|
|
312
323
|
- dontClause (string): What NOT to do (e.g. "Don't instantiate services with new directly")
|
|
313
324
|
- whenClause (string): When this pattern applies (e.g. "When creating a new ViewController subclass")
|
|
314
325
|
- topicHint (string): Group label (e.g. "Networking", "UI-Layout")
|
|
315
|
-
- coreCode (string): Minimal 3-10 line code skeleton that captures the essence
|
|
326
|
+
- coreCode (string): Minimal 3-10 line code skeleton that captures the essence. Must be syntactically complete — balanced brackets/parentheses, never start with } or ) and never end with { or (
|
|
316
327
|
- constraints (object, optional): { "preconditions": ["<conditions that must be true before using this pattern>"], "sideEffects": ["<observable side effects of using this code>"], "boundaries": ["<usage limitations or scope restrictions>"] }
|
|
317
328
|
- aiInsight (string, optional): One-sentence concise insight — the single most important takeaway about this code pattern
|
|
318
329
|
|
|
@@ -334,14 +345,17 @@ ${files}`;
|
|
|
334
345
|
extCounts[ext] = (extCounts[ext] || 0) + 1;
|
|
335
346
|
}
|
|
336
347
|
|
|
348
|
+
// 使用 LanguageService 推断主语言
|
|
349
|
+
const primaryLang = LanguageService.detectPrimary(extCounts);
|
|
337
350
|
const dominant = Object.entries(extCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || '';
|
|
338
351
|
|
|
339
352
|
// iOS/macOS (Swift / Objective-C)
|
|
340
|
-
if (
|
|
353
|
+
if (primaryLang === 'swift' || primaryLang === 'objectivec') {
|
|
341
354
|
return {
|
|
342
|
-
primaryLanguage:
|
|
355
|
+
primaryLanguage: primaryLang,
|
|
343
356
|
role: 'Senior iOS/macOS Architect',
|
|
344
|
-
patternExamples:
|
|
357
|
+
patternExamples:
|
|
358
|
+
'how to set up a ViewController, configure a TableView with delegate/datasource, build a login UI, handle network responses',
|
|
345
359
|
extractionExamples: `Examples of good extractions:
|
|
346
360
|
- Complete \`init\` method with all tabBarItem/navigationItem configuration
|
|
347
361
|
- Complete \`viewDidLoad\` with all setup calls
|
|
@@ -353,11 +367,12 @@ ${files}`;
|
|
|
353
367
|
}
|
|
354
368
|
|
|
355
369
|
// JavaScript / TypeScript
|
|
356
|
-
if (
|
|
370
|
+
if (primaryLang === 'javascript' || primaryLang === 'typescript') {
|
|
357
371
|
return {
|
|
358
|
-
primaryLanguage:
|
|
372
|
+
primaryLanguage: primaryLang,
|
|
359
373
|
role: 'Senior Software Engineer',
|
|
360
|
-
patternExamples:
|
|
374
|
+
patternExamples:
|
|
375
|
+
'Express/Koa middleware, React component patterns, service class with dependency injection, data processing pipeline, error handling wrapper, factory/strategy patterns',
|
|
361
376
|
extractionExamples: `Examples of good extractions:
|
|
362
377
|
- Complete class with constructor and key methods
|
|
363
378
|
- Express route handler with validation and error handling
|
|
@@ -369,11 +384,12 @@ ${files}`;
|
|
|
369
384
|
}
|
|
370
385
|
|
|
371
386
|
// Python
|
|
372
|
-
if (
|
|
387
|
+
if (primaryLang === 'python') {
|
|
373
388
|
return {
|
|
374
389
|
primaryLanguage: 'python',
|
|
375
390
|
role: 'Senior Python Engineer',
|
|
376
|
-
patternExamples:
|
|
391
|
+
patternExamples:
|
|
392
|
+
'Django/Flask views, data processing with pandas, async handlers, decorator patterns, class-based services',
|
|
377
393
|
extractionExamples: `Examples of good extractions:
|
|
378
394
|
- Complete class with __init__ and key methods
|
|
379
395
|
- Decorator factory function
|
|
@@ -385,11 +401,12 @@ ${files}`;
|
|
|
385
401
|
}
|
|
386
402
|
|
|
387
403
|
// Go
|
|
388
|
-
if (
|
|
404
|
+
if (primaryLang === 'go') {
|
|
389
405
|
return {
|
|
390
406
|
primaryLanguage: 'go',
|
|
391
407
|
role: 'Senior Go Engineer',
|
|
392
|
-
patternExamples:
|
|
408
|
+
patternExamples:
|
|
409
|
+
'HTTP handler with middleware, goroutine patterns, interface implementations, struct methods with error handling',
|
|
393
410
|
extractionExamples: `Examples of good extractions:
|
|
394
411
|
- Complete struct with constructor and methods
|
|
395
412
|
- HTTP handler function with error propagation
|
|
@@ -400,11 +417,12 @@ ${files}`;
|
|
|
400
417
|
}
|
|
401
418
|
|
|
402
419
|
// Kotlin / Java
|
|
403
|
-
if (
|
|
420
|
+
if (primaryLang === 'kotlin' || primaryLang === 'java') {
|
|
404
421
|
return {
|
|
405
|
-
primaryLanguage:
|
|
422
|
+
primaryLanguage: primaryLang,
|
|
406
423
|
role: 'Senior Android/Backend Engineer',
|
|
407
|
-
patternExamples:
|
|
424
|
+
patternExamples:
|
|
425
|
+
'Activity/Fragment lifecycle, repository pattern, ViewModel with LiveData, Retrofit service, dependency injection setup',
|
|
408
426
|
extractionExamples: `Examples of good extractions:
|
|
409
427
|
- Complete class with constructor and key methods
|
|
410
428
|
- Repository with CRUD operations
|
|
@@ -416,11 +434,12 @@ ${files}`;
|
|
|
416
434
|
}
|
|
417
435
|
|
|
418
436
|
// Rust
|
|
419
|
-
if (
|
|
437
|
+
if (primaryLang === 'rust') {
|
|
420
438
|
return {
|
|
421
439
|
primaryLanguage: 'rust',
|
|
422
440
|
role: 'Senior Rust Engineer',
|
|
423
|
-
patternExamples:
|
|
441
|
+
patternExamples:
|
|
442
|
+
'trait implementations, error handling with Result, async functions, builder patterns, iterator chains',
|
|
424
443
|
extractionExamples: `Examples of good extractions:
|
|
425
444
|
- Complete impl block with key methods
|
|
426
445
|
- Trait implementation with all required methods
|
|
@@ -432,11 +451,12 @@ ${files}`;
|
|
|
432
451
|
}
|
|
433
452
|
|
|
434
453
|
// Vue
|
|
435
|
-
if (
|
|
454
|
+
if (dominant === 'vue') {
|
|
436
455
|
return {
|
|
437
456
|
primaryLanguage: 'vue',
|
|
438
457
|
role: 'Senior Frontend Engineer',
|
|
439
|
-
patternExamples:
|
|
458
|
+
patternExamples:
|
|
459
|
+
'Vue component with composition API, composable functions, Vuex/Pinia store modules, router guards',
|
|
440
460
|
extractionExamples: `Examples of good extractions:
|
|
441
461
|
- Complete Vue component with setup/template
|
|
442
462
|
- Composable function with reactive state
|
|
@@ -447,11 +467,12 @@ ${files}`;
|
|
|
447
467
|
}
|
|
448
468
|
|
|
449
469
|
// Ruby
|
|
450
|
-
if (
|
|
470
|
+
if (primaryLang === 'ruby') {
|
|
451
471
|
return {
|
|
452
472
|
primaryLanguage: 'ruby',
|
|
453
473
|
role: 'Senior Ruby Engineer',
|
|
454
|
-
patternExamples:
|
|
474
|
+
patternExamples:
|
|
475
|
+
'Rails controller actions, model concerns, service objects, background jobs, API serializers',
|
|
455
476
|
extractionExamples: `Examples of good extractions:
|
|
456
477
|
- Complete controller with CRUD actions
|
|
457
478
|
- Service object with call method
|
|
@@ -465,7 +486,8 @@ ${files}`;
|
|
|
465
486
|
return {
|
|
466
487
|
primaryLanguage: dominant || 'unknown',
|
|
467
488
|
role: 'Senior Software Engineer',
|
|
468
|
-
patternExamples:
|
|
489
|
+
patternExamples:
|
|
490
|
+
'design patterns, service abstractions, data flow handling, error management, configuration setup',
|
|
469
491
|
extractionExamples: `Examples of good extractions:
|
|
470
492
|
- Complete class/function with full implementation
|
|
471
493
|
- Service method with error handling and retries
|
|
@@ -494,17 +516,33 @@ ${files}`;
|
|
|
494
516
|
* 构建 enrichCandidates 提示词
|
|
495
517
|
*/
|
|
496
518
|
_buildEnrichPrompt(candidates) {
|
|
497
|
-
const items = candidates
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
519
|
+
const items = candidates
|
|
520
|
+
.map((c, i) => {
|
|
521
|
+
const existing = [];
|
|
522
|
+
if (c.rationale) {
|
|
523
|
+
existing.push(`rationale: ${c.rationale}`);
|
|
524
|
+
}
|
|
525
|
+
if (c.knowledgeType) {
|
|
526
|
+
existing.push(`knowledgeType: ${c.knowledgeType}`);
|
|
527
|
+
}
|
|
528
|
+
if (c.complexity) {
|
|
529
|
+
existing.push(`complexity: ${c.complexity}`);
|
|
530
|
+
}
|
|
531
|
+
if (c.scope) {
|
|
532
|
+
existing.push(`scope: ${c.scope}`);
|
|
533
|
+
}
|
|
534
|
+
if (c.steps?.length) {
|
|
535
|
+
existing.push(`steps: [${c.steps.length} steps already]`);
|
|
536
|
+
}
|
|
537
|
+
if (c.constraints?.preconditions?.length) {
|
|
538
|
+
existing.push(`preconditions: [${c.constraints.preconditions.length} items]`);
|
|
539
|
+
}
|
|
540
|
+
const existingStr =
|
|
541
|
+
existing.length > 0
|
|
542
|
+
? `\nAlready filled: ${existing.join(', ')}`
|
|
543
|
+
: '\nNo semantic fields filled yet.';
|
|
544
|
+
|
|
545
|
+
return `--- CANDIDATE #${i + 1} ---
|
|
508
546
|
Title: ${c.title || '(untitled)'}
|
|
509
547
|
Language: ${c.language || 'unknown'}
|
|
510
548
|
Category: ${c.category || ''}
|
|
@@ -512,7 +550,8 @@ Description: ${c.description || c.summary || ''}
|
|
|
512
550
|
${existingStr}
|
|
513
551
|
Code:
|
|
514
552
|
${(c.code || '').substring(0, 2000)}`;
|
|
515
|
-
|
|
553
|
+
})
|
|
554
|
+
.join('\n\n');
|
|
516
555
|
|
|
517
556
|
return `# Role
|
|
518
557
|
You are a Senior Software Architect performing deep semantic analysis on code candidates.
|
|
@@ -561,21 +600,28 @@ ${items}`;
|
|
|
561
600
|
_resolveProxyUrl() {
|
|
562
601
|
// Provider-specific vars: ASD_GOOGLE_PROXY_HTTPS, ASD_OPENAI_PROXY_HTTPS, etc.
|
|
563
602
|
const tag = (this.name || '')
|
|
564
|
-
.replace(/-gemini$/, '')
|
|
565
|
-
.replace(/-/g, '_')
|
|
566
|
-
.toUpperCase();
|
|
603
|
+
.replace(/-gemini$/, '') // google-gemini → google
|
|
604
|
+
.replace(/-/g, '_') // 其他连字符 → 下划线
|
|
605
|
+
.toUpperCase(); // google → GOOGLE
|
|
567
606
|
|
|
568
607
|
if (tag) {
|
|
569
|
-
const specific =
|
|
570
|
-
|| process.env[`ASD_${tag}_PROXY_HTTP`];
|
|
571
|
-
if (specific)
|
|
608
|
+
const specific =
|
|
609
|
+
process.env[`ASD_${tag}_PROXY_HTTPS`] || process.env[`ASD_${tag}_PROXY_HTTP`];
|
|
610
|
+
if (specific) {
|
|
611
|
+
return specific;
|
|
612
|
+
}
|
|
572
613
|
}
|
|
573
614
|
|
|
574
|
-
return
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
||
|
|
615
|
+
return (
|
|
616
|
+
process.env.ASD_AI_PROXY ||
|
|
617
|
+
process.env.HTTPS_PROXY ||
|
|
618
|
+
process.env.https_proxy ||
|
|
619
|
+
process.env.HTTP_PROXY ||
|
|
620
|
+
process.env.http_proxy ||
|
|
621
|
+
process.env.ALL_PROXY ||
|
|
622
|
+
process.env.all_proxy ||
|
|
623
|
+
''
|
|
624
|
+
);
|
|
579
625
|
}
|
|
580
626
|
|
|
581
627
|
/**
|
|
@@ -604,11 +650,15 @@ ${items}`;
|
|
|
604
650
|
* 支持截断修复:当 AI 输出被 token 限制截断时,尝试关闭未完成的 JSON 结构
|
|
605
651
|
*/
|
|
606
652
|
extractJSON(text, openChar = '{', closeChar = '}') {
|
|
607
|
-
if (!text)
|
|
653
|
+
if (!text) {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
608
656
|
// 去除 markdown 代码块
|
|
609
657
|
const cleaned = text.replace(/```(?:json)?\s*/gi, '').replace(/```/g, '');
|
|
610
658
|
const start = cleaned.indexOf(openChar);
|
|
611
|
-
if (start === -1)
|
|
659
|
+
if (start === -1) {
|
|
660
|
+
return null;
|
|
661
|
+
}
|
|
612
662
|
const end = cleaned.lastIndexOf(closeChar);
|
|
613
663
|
|
|
614
664
|
// 1. 常规路径:找到完整的 JSON 边界
|
|
@@ -637,11 +687,15 @@ ${items}`;
|
|
|
637
687
|
_repairTruncatedArray(text) {
|
|
638
688
|
// ── 策略 1:字符级深度追踪 ──
|
|
639
689
|
const charResult = this._repairByCharTracking(text);
|
|
640
|
-
if (charResult)
|
|
690
|
+
if (charResult) {
|
|
691
|
+
return charResult;
|
|
692
|
+
}
|
|
641
693
|
|
|
642
694
|
// ── 策略 2:正则回退 — 找所有 "}," 或 "}\n" 位置,从后向前逐一尝试 JSON.parse ──
|
|
643
695
|
const regexResult = this._repairByRegexFallback(text);
|
|
644
|
-
if (regexResult)
|
|
696
|
+
if (regexResult) {
|
|
697
|
+
return regexResult;
|
|
698
|
+
}
|
|
645
699
|
|
|
646
700
|
return null;
|
|
647
701
|
}
|
|
@@ -652,18 +706,30 @@ ${items}`;
|
|
|
652
706
|
_repairByCharTracking(text) {
|
|
653
707
|
let depth = 0;
|
|
654
708
|
let inString = false;
|
|
655
|
-
let
|
|
709
|
+
let isEscaped = false;
|
|
656
710
|
let lastCompleteObjEnd = -1;
|
|
657
711
|
|
|
658
712
|
for (let i = 0; i < text.length; i++) {
|
|
659
713
|
const ch = text[i];
|
|
660
|
-
if (
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
714
|
+
if (isEscaped) {
|
|
715
|
+
isEscaped = false;
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (ch === '\\' && inString) {
|
|
719
|
+
isEscaped = true;
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (ch === '"') {
|
|
723
|
+
inString = !inString;
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
if (inString) {
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
664
729
|
|
|
665
|
-
if (ch === '{' || ch === '[')
|
|
666
|
-
|
|
730
|
+
if (ch === '{' || ch === '[') {
|
|
731
|
+
depth++;
|
|
732
|
+
} else if (ch === '}' || ch === ']') {
|
|
667
733
|
depth--;
|
|
668
734
|
// depth === 1 表示回到数组顶层,刚关闭了一个完整对象
|
|
669
735
|
if (depth === 1 && ch === '}') {
|
|
@@ -672,7 +738,9 @@ ${items}`;
|
|
|
672
738
|
}
|
|
673
739
|
}
|
|
674
740
|
|
|
675
|
-
if (lastCompleteObjEnd === -1)
|
|
741
|
+
if (lastCompleteObjEnd === -1) {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
676
744
|
return this._tryRepairAt(text, lastCompleteObjEnd);
|
|
677
745
|
}
|
|
678
746
|
|
|
@@ -683,7 +751,7 @@ ${items}`;
|
|
|
683
751
|
_repairByRegexFallback(text) {
|
|
684
752
|
// 收集所有 "}" 后跟 "," 或空白的位置(可能是对象边界)
|
|
685
753
|
const candidates = [];
|
|
686
|
-
const re = /\}[\s,]*(?=\s*[
|
|
754
|
+
const re = /\}[\s,]*(?=\s*[[{]|$)/g;
|
|
687
755
|
let m;
|
|
688
756
|
while ((m = re.exec(text)) !== null) {
|
|
689
757
|
candidates.push(m.index); // "}" 的位置
|
|
@@ -692,7 +760,9 @@ ${items}`;
|
|
|
692
760
|
// 从后往前尝试
|
|
693
761
|
for (let i = candidates.length - 1; i >= 0; i--) {
|
|
694
762
|
const result = this._tryRepairAt(text, candidates[i]);
|
|
695
|
-
if (result)
|
|
763
|
+
if (result) {
|
|
764
|
+
return result;
|
|
765
|
+
}
|
|
696
766
|
}
|
|
697
767
|
return null;
|
|
698
768
|
}
|
|
@@ -711,10 +781,15 @@ ${items}`;
|
|
|
711
781
|
try {
|
|
712
782
|
const result = JSON.parse(repaired);
|
|
713
783
|
if (Array.isArray(result) && result.length > 0) {
|
|
714
|
-
this._log(
|
|
784
|
+
this._log(
|
|
785
|
+
'warn',
|
|
786
|
+
`[extractJSON] Repaired truncated JSON array: recovered ${result.length} items from truncated response`
|
|
787
|
+
);
|
|
715
788
|
return result;
|
|
716
789
|
}
|
|
717
|
-
} catch {
|
|
790
|
+
} catch {
|
|
791
|
+
/* this position didn't work, try next */
|
|
792
|
+
}
|
|
718
793
|
return null;
|
|
719
794
|
}
|
|
720
795
|
|
|
@@ -733,7 +808,9 @@ ${items}`;
|
|
|
733
808
|
if (this._circuitState === 'OPEN') {
|
|
734
809
|
const elapsed = Date.now() - (this._circuitOpenedAt || 0);
|
|
735
810
|
if (elapsed < (this._circuitCooldownMs || 30000)) {
|
|
736
|
-
const err = new Error(
|
|
811
|
+
const err = new Error(
|
|
812
|
+
`AI 服务熔断中 (连续 ${this._circuitFailures} 次失败),${Math.ceil(((this._circuitCooldownMs || 30000) - elapsed) / 1000)}s 后恢复`
|
|
813
|
+
);
|
|
737
814
|
err.code = 'CIRCUIT_OPEN';
|
|
738
815
|
throw err;
|
|
739
816
|
}
|
|
@@ -753,27 +830,38 @@ ${items}`;
|
|
|
753
830
|
// ── 综合判断是否为可重试的网络/服务端错误 ──
|
|
754
831
|
const causeCode = err.cause?.code || '';
|
|
755
832
|
// 网络级错误:无 HTTP status,底层连接失败
|
|
756
|
-
const isNetworkError =
|
|
757
|
-
err.
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
833
|
+
const isNetworkError =
|
|
834
|
+
!err.status &&
|
|
835
|
+
(err.message === 'fetch failed' ||
|
|
836
|
+
err.code === 'ECONNRESET' ||
|
|
837
|
+
causeCode === 'ECONNRESET' ||
|
|
838
|
+
err.code === 'ECONNREFUSED' ||
|
|
839
|
+
causeCode === 'ECONNREFUSED' ||
|
|
840
|
+
err.code === 'ENOTFOUND' ||
|
|
841
|
+
causeCode === 'ENOTFOUND' ||
|
|
842
|
+
err.code === 'ECONNABORTED' ||
|
|
843
|
+
causeCode === 'ECONNABORTED' ||
|
|
844
|
+
err.code === 'ETIMEDOUT' ||
|
|
845
|
+
causeCode === 'ETIMEDOUT' ||
|
|
846
|
+
err.code === 'UND_ERR_CONNECT_TIMEOUT' ||
|
|
847
|
+
causeCode === 'UND_ERR_CONNECT_TIMEOUT' ||
|
|
848
|
+
err.code === 'UND_ERR_SOCKET' ||
|
|
849
|
+
causeCode === 'UND_ERR_SOCKET');
|
|
766
850
|
const isRetryable = err.status === 429 || err.status >= 500 || isNetworkError;
|
|
767
851
|
|
|
768
852
|
// 首次失败记录详细诊断(含 cause)
|
|
769
853
|
if (attempt === 0 && (isNetworkError || err.cause)) {
|
|
770
|
-
this._log?.(
|
|
854
|
+
this._log?.(
|
|
855
|
+
'warn',
|
|
856
|
+
`[_withRetry] ${err.message} — cause: ${err.cause?.message || causeCode || 'unknown'}`
|
|
857
|
+
);
|
|
771
858
|
}
|
|
772
859
|
|
|
773
860
|
if (attempt >= retries || !isRetryable) {
|
|
774
861
|
// 只有服务端错误 / 网络错误才累计熔断计数
|
|
775
862
|
// 客户端错误 (4xx 非 429) 不应触发熔断 — 那是请求本身的问题
|
|
776
|
-
const isServerError =
|
|
863
|
+
const isServerError =
|
|
864
|
+
isNetworkError || err.status === 429 || err.status >= 500 || !err.status;
|
|
777
865
|
if (isServerError) {
|
|
778
866
|
this._circuitFailures = (this._circuitFailures || 0) + 1;
|
|
779
867
|
if (this._circuitFailures >= (this._circuitThreshold || 5)) {
|
|
@@ -781,15 +869,21 @@ ${items}`;
|
|
|
781
869
|
this._circuitOpenedAt = Date.now();
|
|
782
870
|
// 先用当前冷却值,再递增给下次: 30s → 60s → 120s(最大 5 分钟)
|
|
783
871
|
const cooldown = this._circuitCooldownMs || 30_000;
|
|
784
|
-
this._log?.(
|
|
872
|
+
this._log?.(
|
|
873
|
+
'warn',
|
|
874
|
+
`[CircuitBreaker] OPEN — ${this._circuitFailures} consecutive failures, cooldown ${cooldown / 1000}s`
|
|
875
|
+
);
|
|
785
876
|
this._circuitCooldownMs = Math.min(cooldown * 2, 300_000);
|
|
786
877
|
}
|
|
787
878
|
}
|
|
788
879
|
throw err;
|
|
789
880
|
}
|
|
790
|
-
const delay = baseDelay *
|
|
791
|
-
this._log?.(
|
|
792
|
-
|
|
881
|
+
const delay = baseDelay * 2 ** attempt + Math.random() * 1000;
|
|
882
|
+
this._log?.(
|
|
883
|
+
'info',
|
|
884
|
+
`[_withRetry] attempt ${attempt + 1} failed (${err.message}), retrying in ${Math.round(delay / 1000)}s…`
|
|
885
|
+
);
|
|
886
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
793
887
|
}
|
|
794
888
|
}
|
|
795
889
|
}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* - tool_result content blocks 用于回传工具执行结果
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { AiProvider } from '../AiProvider.js';
|
|
11
10
|
import Logger from '../../../infrastructure/logging/Logger.js';
|
|
11
|
+
import { AiProvider } from '../AiProvider.js';
|
|
12
12
|
|
|
13
13
|
const CLAUDE_BASE = 'https://api.anthropic.com/v1';
|
|
14
14
|
const ANTHROPIC_VERSION = '2023-06-01';
|
|
@@ -47,7 +47,7 @@ export class ClaudeProvider extends AiProvider {
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const data = await this._post(`${CLAUDE_BASE}/messages`, body);
|
|
50
|
-
const textBlock = (data?.content || []).find(c => c.type === 'text');
|
|
50
|
+
const textBlock = (data?.content || []).find((c) => c.type === 'text');
|
|
51
51
|
return textBlock?.text || '';
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -77,9 +77,8 @@ export class ClaudeProvider extends AiProvider {
|
|
|
77
77
|
} = opts;
|
|
78
78
|
|
|
79
79
|
// 统一消息 → Anthropic Messages 格式
|
|
80
|
-
const srcMessages =
|
|
81
|
-
? unifiedMessages
|
|
82
|
-
: [{ role: 'user', content: prompt }];
|
|
80
|
+
const srcMessages =
|
|
81
|
+
unifiedMessages?.length > 0 ? unifiedMessages : [{ role: 'user', content: prompt }];
|
|
83
82
|
|
|
84
83
|
const messages = this.#convertMessages(srcMessages);
|
|
85
84
|
|
|
@@ -98,7 +97,7 @@ export class ClaudeProvider extends AiProvider {
|
|
|
98
97
|
// 工具声明 + tool_choice
|
|
99
98
|
// toolChoice='none' 时不传 tools(Anthropic 没有 'none' tool_choice)
|
|
100
99
|
if (toolChoice !== 'none' && toolSchemas?.length > 0) {
|
|
101
|
-
body.tools = toolSchemas.map(s => ({
|
|
100
|
+
body.tools = toolSchemas.map((s) => ({
|
|
102
101
|
name: s.name,
|
|
103
102
|
description: s.description || '',
|
|
104
103
|
input_schema: s.parameters || { type: 'object', properties: {} },
|
|
@@ -159,7 +158,9 @@ export class ClaudeProvider extends AiProvider {
|
|
|
159
158
|
i++;
|
|
160
159
|
} else if (msg.role === 'assistant') {
|
|
161
160
|
const content = [];
|
|
162
|
-
if (msg.content)
|
|
161
|
+
if (msg.content) {
|
|
162
|
+
content.push({ type: 'text', text: msg.content });
|
|
163
|
+
}
|
|
163
164
|
if (msg.toolCalls?.length > 0) {
|
|
164
165
|
for (const tc of msg.toolCalls) {
|
|
165
166
|
content.push({
|
|
@@ -232,7 +233,9 @@ export class ClaudeProvider extends AiProvider {
|
|
|
232
233
|
}
|
|
233
234
|
|
|
234
235
|
if (functionCalls.length > 0) {
|
|
235
|
-
this.logger.debug(
|
|
236
|
+
this.logger.debug(
|
|
237
|
+
`[Claude] native function calls: ${functionCalls.map((fc) => fc.name).join(', ')}`
|
|
238
|
+
);
|
|
236
239
|
return {
|
|
237
240
|
text: textParts.length > 0 ? textParts.join('\n') : null,
|
|
238
241
|
functionCalls,
|
|
@@ -249,10 +252,12 @@ export class ClaudeProvider extends AiProvider {
|
|
|
249
252
|
|
|
250
253
|
async summarize(code) {
|
|
251
254
|
const prompt = `请对以下代码生成结构化摘要,返回 JSON 格式 {title, description, language, patterns: [], keyAPIs: []}:\n\n${code}`;
|
|
252
|
-
return
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
255
|
+
return (
|
|
256
|
+
(await this.chatWithStructuredOutput(prompt, {
|
|
257
|
+
temperature: 0.3,
|
|
258
|
+
maxTokens: 4096,
|
|
259
|
+
})) || { title: '', description: '' }
|
|
260
|
+
);
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
async embed(_text) {
|
|
@@ -265,6 +270,14 @@ export class ClaudeProvider extends AiProvider {
|
|
|
265
270
|
}
|
|
266
271
|
|
|
267
272
|
async _post(url, body) {
|
|
273
|
+
if (!this.apiKey) {
|
|
274
|
+
const err = new Error(
|
|
275
|
+
'Claude API Key 未配置。请在 .env 中设置 ASD_CLAUDE_API_KEY,或运行 asd setup 完成配置。'
|
|
276
|
+
);
|
|
277
|
+
err.code = 'API_KEY_MISSING';
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
|
|
268
281
|
const controller = new AbortController();
|
|
269
282
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
270
283
|
|