@jsonstudio/rcc 0.89.164 → 0.89.168

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.
Files changed (30) hide show
  1. package/dist/build-info.js +3 -3
  2. package/dist/build-info.js.map +1 -1
  3. package/dist/providers/core/config/service-profiles.js +15 -1
  4. package/dist/providers/core/config/service-profiles.js.map +1 -1
  5. package/dist/providers/core/runtime/base-provider.d.ts +6 -1
  6. package/dist/providers/core/runtime/base-provider.js +51 -103
  7. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  8. package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +1 -2
  9. package/dist/providers/core/runtime/gemini-cli-http-provider.js +9 -5
  10. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  11. package/dist/providers/core/runtime/gemini-http-provider.d.ts +1 -2
  12. package/dist/providers/core/runtime/gemini-http-provider.js +0 -12
  13. package/dist/providers/core/runtime/gemini-http-provider.js.map +1 -1
  14. package/dist/providers/core/runtime/http-request-executor.d.ts +42 -0
  15. package/dist/providers/core/runtime/http-request-executor.js +133 -0
  16. package/dist/providers/core/runtime/http-request-executor.js.map +1 -0
  17. package/dist/providers/core/runtime/http-transport-provider.d.ts +7 -12
  18. package/dist/providers/core/runtime/http-transport-provider.js +168 -368
  19. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  20. package/dist/providers/core/runtime/provider-error-classifier.d.ts +25 -0
  21. package/dist/providers/core/runtime/provider-error-classifier.js +139 -0
  22. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -0
  23. package/dist/providers/core/runtime/provider-error-types.d.ts +23 -0
  24. package/dist/providers/core/runtime/provider-error-types.js +2 -0
  25. package/dist/providers/core/runtime/provider-error-types.js.map +1 -0
  26. package/dist/providers/core/runtime/provider-factory.js +6 -0
  27. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  28. package/package.json +3 -2
  29. package/scripts/pack-mode.mjs +30 -1
  30. package/scripts/publish-rcc.mjs +31 -0
@@ -16,83 +16,34 @@ import { OAuthAuthProvider } from '../../auth/oauth-auth.js';
16
16
  import { logOAuthDebug } from '../../auth/oauth-logger.js';
17
17
  import { TokenFileAuthProvider } from '../../auth/tokenfile-auth.js';
18
18
  import { ensureValidOAuthToken, handleUpstreamInvalidOAuthToken } from '../../auth/oauth-lifecycle.js';
19
- import { createHookSystemIntegration, HookSystemIntegration } from '../hooks/hooks-integration.js';
20
- import { attachProviderSseSnapshotStream, shouldCaptureProviderStreamSnapshots, writeProviderSnapshot } from '../utils/snapshot-writer.js';
19
+ import { attachProviderSseSnapshotStream, writeProviderSnapshot } from '../utils/snapshot-writer.js';
21
20
  import { attachProviderRuntimeMetadata } from './provider-runtime-metadata.js';
22
21
  import { OpenAIChatProtocolClient } from '../../../client/openai/chat-protocol-client.js';
23
- const ENABLE_PROVIDER_HOOKS = process.env.ROUTECODEX_ENABLE_PROVIDER_HOOKS === '1';
22
+ import { HttpRequestExecutor } from './http-request-executor.js';
23
+ import { extractStatusCodeFromError } from './provider-error-classifier.js';
24
24
  const isRecord = (value) => typeof value === 'object' && value !== null;
25
- function createNoopHookSystemIntegration() {
26
- const passthrough = async (_stage, _target, data) => ({
27
- data,
28
- metrics: {
29
- executionTime: 0,
30
- hookCount: 0,
31
- successCount: 0,
32
- readCount: 0,
33
- writeCount: 0,
34
- transformCount: 0
35
- }
36
- });
37
- return {
38
- getBidirectionalHookManager: () => ({
39
- registerHook: () => { },
40
- unregisterHook: () => { },
41
- executeHookChain: passthrough,
42
- setDebugConfig: () => { }
43
- }),
44
- setDebugConfig: () => { },
45
- initialize: async () => { },
46
- getStats: () => ({ enabled: false }),
47
- healthCheck: async () => ({ healthy: true }),
48
- start: async () => { },
49
- stop: async () => { },
50
- shutdown: async () => { }
51
- };
52
- }
53
- const readStatusCodeFromResponse = (error) => {
54
- const response = error?.response;
55
- const directStatus = typeof response?.status === 'number' ? response.status : undefined;
56
- const directStatusCode = typeof response?.statusCode === 'number'
57
- ? response.statusCode
58
- : undefined;
59
- const nestedStatus = response &&
60
- typeof response === 'object' &&
61
- typeof response?.data?.status === 'number'
62
- ? response.data.status
63
- : undefined;
64
- const nestedErrorStatus = response &&
65
- typeof response === 'object' &&
66
- typeof response?.data?.error?.status === 'number'
67
- ? response.data.error.status
68
- : undefined;
69
- return [directStatus, directStatusCode, nestedStatus, nestedErrorStatus].find((candidate) => typeof candidate === 'number' && Number.isFinite(candidate));
70
- };
71
25
  const DEFAULT_USER_AGENT = 'RouteCodex/2.0';
72
26
  export class HttpTransportProvider extends BaseProvider {
73
27
  type;
74
28
  authProvider = null;
75
29
  httpClient;
76
30
  serviceProfile;
77
- hookSystemIntegration;
78
31
  protocolClient;
32
+ requestExecutor;
79
33
  injectedConfig = null;
80
- hooksEnabled;
81
34
  constructor(config, dependencies, moduleType, protocolClient) {
82
35
  super(config, dependencies);
83
36
  this.type = moduleType;
84
37
  this.protocolClient = protocolClient ?? new OpenAIChatProtocolClient();
85
- this.hooksEnabled = ENABLE_PROVIDER_HOOKS;
86
38
  // 获取服务配置档案
87
39
  this.serviceProfile = this.getServiceProfile();
88
40
  // 验证配置
89
41
  this.validateConfig();
90
42
  // 创建HTTP客户端
91
43
  this.createHttpClient();
44
+ this.requestExecutor = new HttpRequestExecutor(this.httpClient, this.createRequestExecutorDeps());
92
45
  // 创建认证提供者
93
46
  this.authProvider = this.createAuthProvider();
94
- // 初始化Hook系统集成
95
- this.hookSystemIntegration = this.initializeHookSystem();
96
47
  }
97
48
  /**
98
49
  * 确保认证提供者完成初始化(避免 ApiKeyAuthProvider 未初始化导致的报错)
@@ -104,7 +55,8 @@ export class HttpTransportProvider extends BaseProvider {
104
55
  const providerConfig = this.config.config;
105
56
  const extensions = this.getConfigExtensions();
106
57
  const auth = providerConfig.auth;
107
- if (this.normalizeAuthMode(auth.type) === 'oauth') {
58
+ const usesTokenFile = this.authProvider instanceof TokenFileAuthProvider;
59
+ if (this.normalizeAuthMode(auth.type) === 'oauth' && !usesTokenFile) {
108
60
  const oauthAuth = auth;
109
61
  const oauthProviderId = this.ensureOAuthProviderId(oauthAuth, extensions);
110
62
  const forceReauthorize = false;
@@ -163,14 +115,6 @@ export class HttpTransportProvider extends BaseProvider {
163
115
  }
164
116
  }
165
117
  }
166
- if (this.hooksEnabled) {
167
- await this.hookSystemIntegration.initialize();
168
- this.configureHookDebugging();
169
- this.dependencies.logger?.logModule(this.id, 'provider-hook-system-initialized', {
170
- providerType: this.providerType,
171
- integrationEnabled: true
172
- });
173
- }
174
118
  }
175
119
  catch (error) {
176
120
  // 暴露问题,快速失败,便于定位凭证问题
@@ -200,62 +144,6 @@ export class HttpTransportProvider extends BaseProvider {
200
144
  getConfig() {
201
145
  return this.injectedConfig ?? this.config.config ?? null;
202
146
  }
203
- /**
204
- * 初始化Hook系统集成
205
- */
206
- initializeHookSystem() {
207
- if (!this.hooksEnabled) {
208
- return createNoopHookSystemIntegration();
209
- }
210
- return createHookSystemIntegration(this.dependencies, this.id, {
211
- enabled: true,
212
- debugMode: true,
213
- snapshotEnabled: true,
214
- migrationMode: true
215
- });
216
- }
217
- /**
218
- * 配置Hook调试(保持向后兼容)
219
- */
220
- configureHookDebugging() {
221
- if (!this.hooksEnabled) {
222
- return;
223
- }
224
- try {
225
- // 设置调试配置(使用统一Hook系统的阶段字符串)
226
- const debugConfig = {
227
- enabled: true,
228
- level: 'verbose',
229
- maxDataSize: 1024 * 64, // 64KB 单次输出上限,避免过大控制台噪声
230
- stages: [
231
- 'request_preprocessing',
232
- 'request_validation',
233
- 'authentication',
234
- 'http_request',
235
- 'http_response',
236
- 'response_validation',
237
- 'response_postprocessing',
238
- 'error_handling'
239
- ],
240
- outputFormat: 'structured',
241
- outputTargets: ['console'],
242
- performanceThresholds: {
243
- maxHookExecutionTime: 500, // 单个Hook 500ms告警
244
- maxTotalExecutionTime: 5000, // 阶段总时长 5s 告警
245
- maxDataSize: 1024 * 256 // 256KB 数据告警
246
- }
247
- };
248
- this.hookSystemIntegration.setDebugConfig(debugConfig);
249
- this.dependencies.logger?.logModule(this.id, 'provider-debug-hooks-configured', {
250
- providerType: this.providerType
251
- });
252
- }
253
- catch (error) {
254
- this.dependencies.logger?.logModule(this.id, 'provider-debug-hooks-error', {
255
- error: error instanceof Error ? error.message : String(error)
256
- });
257
- }
258
- }
259
147
  getServiceProfile() {
260
148
  const cfg = this.config.config;
261
149
  const profileKey = this.resolveProfileKey(cfg);
@@ -349,13 +237,17 @@ export class HttpTransportProvider extends BaseProvider {
349
237
  const auth = this.config.config.auth;
350
238
  const extensions = this.getConfigExtensions();
351
239
  const authMode = this.normalizeAuthMode(auth.type);
352
- const providerIdForAuth = authMode === 'oauth'
240
+ this.authMode = authMode;
241
+ let providerIdForAuth = authMode === 'oauth'
353
242
  ? this.ensureOAuthProviderId(auth, extensions)
354
243
  : this.providerType;
355
- // 验证认证配置(按 providerIdForAuth 选择服务档案)
356
- // 对于 gemini-cli 这类变体,provider 行为归入 gemini 协议族
357
- const profileKeyForValidation = providerIdForAuth === 'gemini-cli' ? 'gemini' : providerIdForAuth;
358
- const validation = ServiceProfileValidator.validateServiceProfile(profileKeyForValidation, authMode);
244
+ if (this.type === 'gemini-cli-http-provider') {
245
+ providerIdForAuth = 'gemini-cli';
246
+ }
247
+ if (authMode === 'oauth') {
248
+ this.oauthProviderId = providerIdForAuth;
249
+ }
250
+ const validation = ServiceProfileValidator.validateServiceProfile(providerIdForAuth, authMode);
359
251
  if (!validation.isValid) {
360
252
  throw new Error(`Invalid auth configuration for ${providerIdForAuth}: ${validation.errors.join(', ')}`);
361
253
  }
@@ -405,6 +297,27 @@ export class HttpTransportProvider extends BaseProvider {
405
297
  }
406
298
  });
407
299
  }
300
+ createRequestExecutorDeps() {
301
+ return {
302
+ wantsUpstreamSse: this.wantsUpstreamSse.bind(this),
303
+ getEffectiveEndpoint: () => this.getEffectiveEndpoint(),
304
+ resolveRequestEndpoint: this.resolveRequestEndpoint.bind(this),
305
+ buildRequestHeaders: this.buildRequestHeaders.bind(this),
306
+ finalizeRequestHeaders: this.finalizeRequestHeaders.bind(this),
307
+ applyStreamModeHeaders: this.applyStreamModeHeaders.bind(this),
308
+ getEffectiveBaseUrl: () => this.getEffectiveBaseUrl(),
309
+ buildHttpRequestBody: this.buildHttpRequestBody.bind(this),
310
+ prepareSseRequestBody: this.prepareSseRequestBody.bind(this),
311
+ getEntryEndpointFromPayload: this.getEntryEndpointFromPayload.bind(this),
312
+ getClientRequestIdFromContext: this.getClientRequestIdFromContext.bind(this),
313
+ wrapUpstreamSseResponse: this.wrapUpstreamSseResponse.bind(this),
314
+ getHttpRetryLimit: () => this.getHttpRetryLimit(),
315
+ shouldRetryHttpError: this.shouldRetryHttpError.bind(this),
316
+ delayBeforeHttpRetry: this.delayBeforeHttpRetry.bind(this),
317
+ tryRecoverOAuthAndReplay: this.tryRecoverOAuthAndReplay.bind(this),
318
+ normalizeHttpError: this.normalizeHttpError.bind(this)
319
+ };
320
+ }
408
321
  async preprocessRequest(request) {
409
322
  const context = this.createProviderContext();
410
323
  const runtimeMetadata = context.runtimeMetadata;
@@ -447,36 +360,12 @@ export class HttpTransportProvider extends BaseProvider {
447
360
  }
448
361
  }
449
362
  catch { /* ignore */ }
450
- // 获取Hook管理器(新的统一系统)
451
- const hookManager = this.getHookManager();
452
- // 🔍 Hook 1: 请求预处理阶段
453
- const preprocessResult = await hookManager.executeHookChain('request_preprocessing', 'request', processedRequest, context);
454
- processedRequest = preprocessResult.data;
455
- ensureRuntimeMetadata(processedRequest);
456
- // 🔍 Hook 2: 请求验证阶段
457
- const validationResult = await hookManager.executeHookChain('request_validation', 'request', processedRequest, context);
458
- processedRequest = validationResult.data;
459
- ensureRuntimeMetadata(processedRequest);
460
- // Provider 层不再修改工具 schema;统一入口在 llmswitch-core/兼容层
461
- // Provider 层不做协议兼容改写:compatibility 由 llmswitch-core Hub Pipeline 统一处理。
462
363
  return processedRequest;
463
364
  }
464
365
  async postprocessResponse(response, context) {
465
366
  const runtime = this.getRuntimeProfile();
466
367
  const processingTime = Date.now() - context.startTime;
467
368
  let processedResponse = response;
468
- // 获取Hook管理器(新的统一系统)
469
- const hookManager = this.getHookManager();
470
- // 🔍 Hook 3: HTTP响应阶段
471
- const httpResponseResult = await hookManager.executeHookChain('http_response', 'response', processedResponse, context);
472
- processedResponse = httpResponseResult.data;
473
- // 🔍 Hook 4: 响应验证阶段
474
- const validationResult = await hookManager.executeHookChain('response_validation', 'response', processedResponse, context);
475
- processedResponse = validationResult.data;
476
- // 🔍 Hook 5: 响应后处理阶段
477
- const postprocessResult = await hookManager.executeHookChain('response_postprocessing', 'response', processedResponse, context);
478
- processedResponse = postprocessResult.data;
479
- // Provider 层不做协议兼容改写:compatibility 由 llmswitch-core Hub Pipeline 统一处理。
480
369
  const originalRecord = this.asResponseRecord(response);
481
370
  const processedRecord = this.asResponseRecord(processedResponse);
482
371
  const sseStream = processedRecord.__sse_responses ||
@@ -494,221 +383,13 @@ export class HttpTransportProvider extends BaseProvider {
494
383
  providerType: this.providerType,
495
384
  // 对外暴露的 model 统一为入站模型
496
385
  model: context.model ?? this.extractModel(processedRecord) ?? this.extractModel(originalRecord),
497
- usage: this.extractUsage(processedRecord) ?? this.extractUsage(originalRecord),
498
- hookMetrics: {
499
- httpResponse: httpResponseResult.metrics,
500
- validation: validationResult.metrics,
501
- postprocess: postprocessResult.metrics
502
- }
386
+ usage: this.extractUsage(processedRecord) ?? this.extractUsage(originalRecord)
503
387
  }
504
388
  };
505
389
  }
506
390
  async sendRequestInternal(request) {
507
391
  const context = this.createProviderContext();
508
- // 获取Hook管理器(新的统一系统)
509
- const hookManager = this.getHookManager();
510
- // 🔍 Hook 8: HTTP请求阶段
511
- const httpRequestResult = await hookManager.executeHookChain('http_request', 'request', request, context);
512
- const processedRequest = httpRequestResult.data;
513
- const wantsSse = this.wantsUpstreamSse(processedRequest, context);
514
- // 仅传入 endpoint,让 HttpClient 按 baseUrl 进行拼接;避免 full URL 再次拼接导致 /https:/ 重复
515
- const defaultEndpoint = this.getEffectiveEndpoint();
516
- const endpoint = this.resolveRequestEndpoint(processedRequest, defaultEndpoint);
517
- const headers = await this.buildRequestHeaders();
518
- let finalHeaders = await this.finalizeRequestHeaders(headers, processedRequest);
519
- finalHeaders = this.applyStreamModeHeaders(finalHeaders, wantsSse);
520
- const targetUrl = `${this.getEffectiveBaseUrl().replace(/\/$/, '')}/${endpoint.startsWith('/') ? endpoint.slice(1) : endpoint}`;
521
- // Flatten request body to standard OpenAI Chat JSON
522
- const finalBody = this.buildHttpRequestBody(processedRequest);
523
- if (wantsSse) {
524
- this.prepareSseRequestBody(finalBody, context);
525
- }
526
- const entryEndpoint = this.getEntryEndpointFromPayload(processedRequest);
527
- const clientRequestId = this.getClientRequestIdFromContext(context);
528
- // 快照:provider-request(默认开启,脱敏headers)
529
- try {
530
- await writeProviderSnapshot({
531
- phase: 'provider-request',
532
- requestId: context.requestId,
533
- data: finalBody,
534
- headers: finalHeaders,
535
- url: targetUrl,
536
- entryEndpoint,
537
- clientRequestId
538
- });
539
- }
540
- catch { /* non-blocking */ }
541
- // 发送HTTP请求(根据是否需要 SSE 决定传输模式)
542
- let response;
543
- const captureSse = shouldCaptureProviderStreamSnapshots();
544
- try {
545
- if (wantsSse) {
546
- const upstreamStream = await this.httpClient.postStream(endpoint, finalBody, finalHeaders);
547
- const streamForHost = captureSse
548
- ? attachProviderSseSnapshotStream(upstreamStream, {
549
- requestId: context.requestId,
550
- headers: finalHeaders,
551
- url: targetUrl,
552
- entryEndpoint,
553
- clientRequestId
554
- })
555
- : upstreamStream;
556
- response = await this.wrapUpstreamSseResponse(streamForHost, context);
557
- if (!captureSse) {
558
- try {
559
- await writeProviderSnapshot({
560
- phase: 'provider-response',
561
- requestId: context.requestId,
562
- data: { mode: 'sse' },
563
- headers: finalHeaders,
564
- url: targetUrl,
565
- entryEndpoint,
566
- clientRequestId
567
- });
568
- }
569
- catch { /* non-blocking */ }
570
- }
571
- }
572
- else {
573
- response = await this.httpClient.post(endpoint, finalBody, finalHeaders);
574
- try {
575
- await writeProviderSnapshot({
576
- phase: 'provider-response',
577
- requestId: context.requestId,
578
- data: response,
579
- headers: finalHeaders,
580
- url: targetUrl,
581
- entryEndpoint,
582
- clientRequestId
583
- });
584
- }
585
- catch { /* non-blocking */ }
586
- }
587
- }
588
- catch (error) {
589
- // OAuth token 失效:尝试刷新/重获并重试一次
590
- try {
591
- const providerAuth = this.config.config.auth;
592
- if (this.normalizeAuthMode(providerAuth.type) === 'oauth') {
593
- const shouldRetry = await handleUpstreamInvalidOAuthToken(this.providerType, providerAuth, error);
594
- if (shouldRetry) {
595
- const retryHeaders = await this.buildRequestHeaders();
596
- let finalRetryHeaders = await this.finalizeRequestHeaders(retryHeaders, processedRequest);
597
- finalRetryHeaders = this.applyStreamModeHeaders(finalRetryHeaders, wantsSse);
598
- if (wantsSse) {
599
- const upstreamStream = await this.httpClient.postStream(endpoint, finalBody, finalRetryHeaders);
600
- const streamForHost = captureSse
601
- ? attachProviderSseSnapshotStream(upstreamStream, {
602
- requestId: context.requestId,
603
- headers: finalRetryHeaders,
604
- url: targetUrl,
605
- entryEndpoint,
606
- clientRequestId,
607
- extra: { retry: true }
608
- })
609
- : upstreamStream;
610
- const wrapped = await this.wrapUpstreamSseResponse(streamForHost, context);
611
- if (!captureSse) {
612
- try {
613
- await writeProviderSnapshot({
614
- phase: 'provider-response',
615
- requestId: context.requestId,
616
- data: { mode: 'sse', retry: true },
617
- headers: finalRetryHeaders,
618
- url: targetUrl,
619
- entryEndpoint,
620
- clientRequestId
621
- });
622
- }
623
- catch { /* non-blocking */ }
624
- }
625
- return wrapped;
626
- }
627
- response = await this.httpClient.post(endpoint, finalBody, finalRetryHeaders);
628
- try {
629
- await writeProviderSnapshot({
630
- phase: 'provider-response',
631
- requestId: context.requestId,
632
- data: response,
633
- headers: finalRetryHeaders,
634
- url: targetUrl,
635
- entryEndpoint,
636
- clientRequestId
637
- });
638
- }
639
- catch { /* non-blocking */ }
640
- return response;
641
- }
642
- }
643
- }
644
- catch { /* ignore and fallthrough */ }
645
- // 🔍 Hook 9: 错误处理阶段
646
- const errorResult = await hookManager.executeHookChain('error_handling', 'error', { error, request: processedRequest, url: targetUrl, headers: finalHeaders }, context);
647
- // 如果Hook处理了错误,使用Hook的返回结果
648
- const hookErrorData = errorResult.data;
649
- if (hookErrorData && hookErrorData.error === false) {
650
- return hookErrorData;
651
- }
652
- // 规范化错误:补充结构化字段,移除仅文本填充的旧做法
653
- const normalized = error;
654
- try {
655
- const msg = typeof normalized.message === 'string' ? normalized.message : String(normalized || '');
656
- const m = msg.match(/HTTP\s+(\d{3})/i);
657
- const parsedStatus = m ? parseInt(m[1], 10) : undefined;
658
- const responseStatus = readStatusCodeFromResponse(normalized);
659
- const statusCode = Number.isFinite(normalized.statusCode)
660
- ? Number(normalized.statusCode)
661
- : Number.isFinite(normalized.status)
662
- ? Number(normalized.status)
663
- : responseStatus ?? parsedStatus ?? undefined;
664
- if (statusCode && !Number.isNaN(statusCode)) {
665
- normalized.statusCode = statusCode;
666
- if (!normalized.status) {
667
- normalized.status = statusCode;
668
- }
669
- if (!normalized.code) {
670
- normalized.code = `HTTP_${statusCode}`;
671
- }
672
- }
673
- // 兼容 Manager 的 code 路径(response.data.error.code)
674
- if (!normalized.response) {
675
- normalized.response = {};
676
- }
677
- if (!normalized.response.data) {
678
- normalized.response.data = {};
679
- }
680
- if (!normalized.response.data.error) {
681
- normalized.response.data.error = {};
682
- }
683
- if (normalized.code && !normalized.response.data.error.code) {
684
- normalized.response.data.error.code = normalized.code;
685
- }
686
- }
687
- catch { /* keep original */ }
688
- // 快照:provider-error(结构化写入)
689
- try {
690
- await writeProviderSnapshot({
691
- phase: 'provider-error',
692
- requestId: context.requestId,
693
- data: {
694
- status: normalized?.statusCode ?? normalized?.status ?? null,
695
- code: normalized?.code ?? null,
696
- error: typeof normalized?.message === 'string' ? normalized.message : String(normalized || '')
697
- },
698
- headers: finalHeaders,
699
- url: targetUrl,
700
- entryEndpoint,
701
- clientRequestId
702
- });
703
- }
704
- catch { /* non-blocking */ }
705
- throw normalized;
706
- }
707
- // Provider 不处理工具修复/注入逻辑:统一收敛到 llmswitch-core 与兼容层
708
- // 此处不做任何自动修复/重试,保持单次请求的幂等与可观测性
709
- try { /* no-op */ }
710
- catch { /* ignore */ }
711
- return response;
392
+ return this.requestExecutor.execute(request, context);
712
393
  }
713
394
  wantsUpstreamSse(_request, _context) {
714
395
  return false;
@@ -744,6 +425,135 @@ export class HttpTransportProvider extends BaseProvider {
744
425
  return false;
745
426
  }
746
427
  }
428
+ getHttpRetryLimit() {
429
+ return 3;
430
+ }
431
+ async delayBeforeHttpRetry(attempt) {
432
+ const delay = Math.min(500 * attempt, 2000);
433
+ await new Promise((resolve) => setTimeout(resolve, delay));
434
+ }
435
+ shouldRetryHttpError(error, attempt, maxAttempts) {
436
+ if (attempt >= maxAttempts) {
437
+ return false;
438
+ }
439
+ const normalized = error;
440
+ const statusCode = extractStatusCodeFromError(normalized);
441
+ if (statusCode && statusCode >= 500) {
442
+ return true;
443
+ }
444
+ return false;
445
+ }
446
+ async tryRecoverOAuthAndReplay(error, requestInfo, processedRequest, captureSse, context) {
447
+ try {
448
+ const providerAuth = this.config.config.auth;
449
+ if (this.normalizeAuthMode(providerAuth.type) !== 'oauth') {
450
+ return undefined;
451
+ }
452
+ const shouldRetry = await handleUpstreamInvalidOAuthToken(this.oauthProviderId || this.providerType, providerAuth, error);
453
+ if (!shouldRetry) {
454
+ return undefined;
455
+ }
456
+ const retryHeaders = await this.buildRequestHeaders();
457
+ let finalRetryHeaders = await this.finalizeRequestHeaders(retryHeaders, processedRequest);
458
+ finalRetryHeaders = this.applyStreamModeHeaders(finalRetryHeaders, requestInfo.wantsSse);
459
+ if (requestInfo.wantsSse) {
460
+ const upstreamStream = await this.httpClient.postStream(requestInfo.endpoint, requestInfo.body, finalRetryHeaders);
461
+ const streamForHost = captureSse
462
+ ? attachProviderSseSnapshotStream(upstreamStream, {
463
+ requestId: context.requestId,
464
+ headers: finalRetryHeaders,
465
+ url: requestInfo.targetUrl,
466
+ entryEndpoint: requestInfo.entryEndpoint,
467
+ clientRequestId: requestInfo.clientRequestId,
468
+ extra: { retry: true }
469
+ })
470
+ : upstreamStream;
471
+ const wrapped = await this.wrapUpstreamSseResponse(streamForHost, context);
472
+ if (!captureSse) {
473
+ try {
474
+ await writeProviderSnapshot({
475
+ phase: 'provider-response',
476
+ requestId: context.requestId,
477
+ data: { mode: 'sse', retry: true },
478
+ headers: finalRetryHeaders,
479
+ url: requestInfo.targetUrl,
480
+ entryEndpoint: requestInfo.entryEndpoint,
481
+ clientRequestId: requestInfo.clientRequestId
482
+ });
483
+ }
484
+ catch { /* non-blocking */ }
485
+ }
486
+ return wrapped;
487
+ }
488
+ const response = await this.httpClient.post(requestInfo.endpoint, requestInfo.body, finalRetryHeaders);
489
+ try {
490
+ await writeProviderSnapshot({
491
+ phase: 'provider-response',
492
+ requestId: context.requestId,
493
+ data: response,
494
+ headers: finalRetryHeaders,
495
+ url: requestInfo.targetUrl,
496
+ entryEndpoint: requestInfo.entryEndpoint,
497
+ clientRequestId: requestInfo.clientRequestId
498
+ });
499
+ }
500
+ catch { /* non-blocking */ }
501
+ return response;
502
+ }
503
+ catch {
504
+ return undefined;
505
+ }
506
+ }
507
+ async normalizeHttpError(error, processedRequest, requestInfo, context) {
508
+ const normalized = error;
509
+ try {
510
+ const statusCode = extractStatusCodeFromError(normalized);
511
+ if (statusCode && !Number.isNaN(statusCode)) {
512
+ normalized.statusCode = statusCode;
513
+ if (!normalized.status) {
514
+ normalized.status = statusCode;
515
+ }
516
+ if (!normalized.code) {
517
+ normalized.code = `HTTP_${statusCode}`;
518
+ }
519
+ }
520
+ if (!normalized.response) {
521
+ normalized.response = {};
522
+ }
523
+ if (!normalized.response.data) {
524
+ normalized.response.data = {};
525
+ }
526
+ if (!normalized.response.data.error) {
527
+ normalized.response.data.error = {};
528
+ }
529
+ if (normalized.code && !normalized.response.data.error.code) {
530
+ normalized.response.data.error.code = normalized.code;
531
+ }
532
+ if (normalized.message && !normalized.response.data.error.message) {
533
+ normalized.response.data.error.message = normalized.message;
534
+ }
535
+ }
536
+ catch {
537
+ /* ignore */
538
+ }
539
+ try {
540
+ await writeProviderSnapshot({
541
+ phase: 'provider-error',
542
+ requestId: context.requestId,
543
+ data: {
544
+ status: normalized?.statusCode ?? normalized?.status ?? null,
545
+ code: normalized?.code ?? null,
546
+ error: typeof normalized?.message === 'string' ? normalized.message : String(error || '')
547
+ },
548
+ headers: requestInfo.headers,
549
+ url: requestInfo.targetUrl,
550
+ entryEndpoint: requestInfo.entryEndpoint ?? this.getEntryEndpointFromPayload(processedRequest),
551
+ clientRequestId: requestInfo.clientRequestId ?? this.getClientRequestIdFromContext(context)
552
+ });
553
+ }
554
+ catch { /* non-blocking */ }
555
+ return normalized;
556
+ }
747
557
  /**
748
558
  * 为特定请求确定最终 endpoint(默认使用配置值,可由子类覆写)
749
559
  */
@@ -888,13 +698,6 @@ export class HttpTransportProvider extends BaseProvider {
888
698
  if (resolvedOriginator) {
889
699
  setHeader(finalHeaders, 'originator', resolvedOriginator);
890
700
  }
891
- // 获取Hook管理器(新的统一系统)
892
- const hookManager = this.getHookManager();
893
- // 🔍 Hook 6: 认证阶段
894
- await hookManager.executeHookChain('authentication', 'auth', authHeaders, this.createProviderContext());
895
- // 🔍 Hook 7: Headers处理阶段
896
- const headersResult = await hookManager.executeHookChain('request_preprocessing', 'headers', finalHeaders, this.createProviderContext());
897
- finalHeaders = headersResult.data;
898
701
  return finalHeaders;
899
702
  }
900
703
  getEffectiveBaseUrl() {
@@ -934,9 +737,6 @@ export class HttpTransportProvider extends BaseProvider {
934
737
  const trimmed = value.trim();
935
738
  return /^https?:\/\//i.test(trimmed) || trimmed.startsWith('//');
936
739
  }
937
- getHookManager() {
938
- return this.hookSystemIntegration.getBidirectionalHookManager();
939
- }
940
740
  // (工具自动修复辅助函数已删除)
941
741
  getConfigExtensions() {
942
742
  const extensions = this.config.config.extensions;