refactor: consolidate single-source routing and protocol helpers
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
# Single Source Consolidation PR1 Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Land PR1 for the highest-value duplication and double-source issues around responses normalization, shared proxy surface orchestration, provider runtime headers, token-route contract sharing, account mutation workflow, and standard API platform helpers.
|
||||
|
||||
**Architecture:** Keep PR1 focused on extracting shared single-source modules and rewiring existing callers to consume them. Do not expand this PR into schema metamodel rewrites, `chatFormatsCore` breakup, or mobile page scaffold work; those remain follow-up PR scope once the highest-risk protocol and workflow duplications are removed.
|
||||
|
||||
**Tech Stack:** TypeScript, Fastify, Vitest, shared server/web modules under `src/shared`, server proxy-core surfaces/providers/services, React admin pages.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Savepoint Commit
|
||||
|
||||
**Files:**
|
||||
- Commit: `src/server/config.ts`
|
||||
- Commit: `src/server/config.test.ts`
|
||||
- Commit: `src/server/db/proxyFileSchemaCompatibility.ts`
|
||||
- Commit: `src/server/db/proxyFileSchemaCompatibility.test.ts`
|
||||
|
||||
**Step 1: Verify the savepoint changes**
|
||||
|
||||
Run: `npm test -- src/server/config.test.ts src/server/db/proxyFileSchemaCompatibility.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
**Step 2: Commit the savepoint**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add src/server/config.ts src/server/config.test.ts src/server/db/proxyFileSchemaCompatibility.ts src/server/db/proxyFileSchemaCompatibility.test.ts
|
||||
git commit -m "fix: trust proxy headers and align proxy file mysql indexes"
|
||||
```
|
||||
|
||||
**Step 3: Cherry-pick into PR1 branch**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git cherry-pick <savepoint-commit>
|
||||
```
|
||||
|
||||
Expected: first commit preserved inside PR1 branch before the larger consolidation commit.
|
||||
|
||||
### Task 2: Responses Single Source
|
||||
|
||||
**Files:**
|
||||
- Create: `src/server/transformers/openai/responses/normalization.ts`
|
||||
- Modify: `src/server/transformers/openai/responses/conversion.ts`
|
||||
- Modify: `src/server/transformers/openai/responses/compatibility.ts`
|
||||
- Test: `src/server/transformers/openai/responses/conversion.test.ts`
|
||||
|
||||
**Step 1: Move shared normalization helpers into one source**
|
||||
|
||||
Keep `normalizeResponsesInputForCompatibility`, `normalizeResponsesMessageContent`, and related block normalization in `normalization.ts`.
|
||||
|
||||
**Step 2: Rewire both callers**
|
||||
|
||||
Make `conversion.ts` and `compatibility.ts` import the shared helpers instead of maintaining their own independent copies.
|
||||
|
||||
**Step 3: Verify parity**
|
||||
|
||||
Run: `npm test -- src/server/transformers/openai/responses/conversion.test.ts`
|
||||
Expected: PASS, including assertions that compatibility exports point at the shared implementation.
|
||||
|
||||
### Task 3: Shared Surface Orchestration
|
||||
|
||||
**Files:**
|
||||
- Create: `src/server/proxy-core/surfaces/sharedSurface.ts`
|
||||
- Modify: `src/server/proxy-core/surfaces/chatSurface.ts`
|
||||
- Modify: `src/server/proxy-core/surfaces/openAiResponsesSurface.ts`
|
||||
- Modify: `src/server/proxy-core/surfaces/geminiSurface.ts`
|
||||
- Test: `src/server/proxy-core/surfaces/sharedSurface.test.ts`
|
||||
|
||||
**Step 1: Extract common orchestration primitives**
|
||||
|
||||
Centralize channel selection, dispatch, proxy logging, retry/failure handling, and common failure response shaping in `sharedSurface.ts`.
|
||||
|
||||
**Step 2: Rewire surface callers**
|
||||
|
||||
Make chat and responses surfaces consume the shared orchestration helpers instead of each keeping a parallel flow.
|
||||
|
||||
**Step 3: Verify behavior**
|
||||
|
||||
Run: `npm test -- src/server/proxy-core/surfaces/sharedSurface.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
### Task 4: Provider Header Builders
|
||||
|
||||
**Files:**
|
||||
- Create: `src/server/proxy-core/providers/headerUtils.ts`
|
||||
- Modify: `src/server/proxy-core/providers/codexProviderProfile.ts`
|
||||
- Modify: `src/server/proxy-core/providers/claudeProviderProfile.ts`
|
||||
- Modify: `src/server/proxy-core/providers/geminiCliProviderProfile.ts`
|
||||
- Modify: `src/server/routes/proxy/upstreamEndpoint.ts`
|
||||
- Test: `src/server/proxy-core/providers/headerUtils.test.ts`
|
||||
|
||||
**Step 1: Extract header parsing and runtime header construction**
|
||||
|
||||
Keep Codex, Claude, and Gemini CLI runtime header shaping in `headerUtils.ts`.
|
||||
|
||||
**Step 2: Remove duplicate builders**
|
||||
|
||||
Make both provider profiles and `upstreamEndpoint.ts` call the shared header builders.
|
||||
|
||||
**Step 3: Verify behavior**
|
||||
|
||||
Run: `npm test -- src/server/proxy-core/providers/headerUtils.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
### Task 5: Cross-Layer Token Route Contract
|
||||
|
||||
**Files:**
|
||||
- Create: `src/shared/tokenRouteContract.js`
|
||||
- Create: `src/shared/tokenRouteContract.d.ts`
|
||||
- Create: `src/shared/tokenRoutePatterns.js`
|
||||
- Create: `src/shared/tokenRoutePatterns.d.ts`
|
||||
- Modify: `src/server/services/tokenRouter.ts`
|
||||
- Modify: `src/server/routes/api/tokens.ts`
|
||||
- Modify: `src/web/pages/token-routes/utils.ts`
|
||||
- Modify: `src/web/pages/token-routes/types.ts`
|
||||
- Modify: `src/web/pages/helpers/routeListVisibility.ts`
|
||||
- Modify: `src/web/pages/Settings.tsx`
|
||||
- Test: `src/shared/tokenRouteContract.test.ts`
|
||||
- Test: `src/shared/tokenRoutePatterns.test.ts`
|
||||
|
||||
**Step 1: Extract shared route mode and pattern matching**
|
||||
|
||||
Make route mode normalization and model pattern matching authoritative in `src/shared`.
|
||||
|
||||
**Step 2: Rewire server and web**
|
||||
|
||||
Make token router, route APIs, token-routes helpers, and settings consume the shared contract instead of maintaining separate copies.
|
||||
|
||||
**Step 3: Verify behavior**
|
||||
|
||||
Run: `npm test -- src/shared/tokenRouteContract.test.ts src/shared/tokenRoutePatterns.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
### Task 6: Account Mutation Workflow
|
||||
|
||||
**Files:**
|
||||
- Create: `src/server/services/accountMutationWorkflow.ts`
|
||||
- Modify: `src/server/routes/api/accounts.ts`
|
||||
- Modify: `src/server/routes/api/accountTokens.ts`
|
||||
- Test: `src/server/services/accountMutationWorkflow.test.ts`
|
||||
|
||||
**Step 1: Extract convergence workflow**
|
||||
|
||||
Move token sync, default token ensure, balance refresh, model refresh, and route rebuild sequencing into `accountMutationWorkflow.ts`.
|
||||
|
||||
**Step 2: Rewire controllers**
|
||||
|
||||
Make account and account-token APIs call the workflow service instead of each owning private orchestration logic.
|
||||
|
||||
**Step 3: Verify behavior**
|
||||
|
||||
Run: `npm test -- src/server/services/accountMutationWorkflow.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
### Task 7: Standard API Provider Base
|
||||
|
||||
**Files:**
|
||||
- Create: `src/server/services/platforms/standardApiProvider.ts`
|
||||
- Modify: `src/server/services/platforms/openai.ts`
|
||||
- Modify: `src/server/services/platforms/claude.ts`
|
||||
- Modify: `src/server/services/platforms/gemini.ts`
|
||||
- Modify: `src/server/services/platforms/cliproxyapi.ts`
|
||||
- Test: `src/server/services/platforms/standardApiProvider.test.ts`
|
||||
- Test: `src/server/services/platforms/llmUpstream.test.ts`
|
||||
|
||||
**Step 1: Extract shared standard API adapter logic**
|
||||
|
||||
Centralize base URL normalization, `/v1/models` resolution, unsupported login/checkin/balance defaults, and common model fetch behavior.
|
||||
|
||||
**Step 2: Rewire adapters**
|
||||
|
||||
Make OpenAI, Claude, Gemini, and CLIProxyAPI adapters extend the shared base instead of repeating the same template logic.
|
||||
|
||||
**Step 3: Verify behavior**
|
||||
|
||||
Run: `npm test -- src/server/services/platforms/standardApiProvider.test.ts src/server/services/platforms/llmUpstream.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
### Task 8: Shared Input File Resolution And Web Helpers
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/server/services/proxyInputFileResolver.ts`
|
||||
- Modify: `src/server/services/proxyInputFileResolver.test.ts`
|
||||
- Modify: `src/server/proxy-core/surfaces/inputFilesSurface.ts`
|
||||
- Create: `src/web/pages/helpers/accountConnection.ts`
|
||||
- Create: `src/web/pages/helpers/accountConnection.test.ts`
|
||||
- Modify: `src/web/pages/Accounts.tsx`
|
||||
- Modify: `src/web/pages/Tokens.tsx`
|
||||
- Modify: `src/web/api.ts`
|
||||
- Modify: `src/web/api.test.ts`
|
||||
|
||||
**Step 1: Make route-level file inlining reuse the service implementation**
|
||||
|
||||
Replace the duplicate `inputFilesSurface.ts` implementation with a re-export of the shared resolver service.
|
||||
|
||||
**Step 2: Extract shared web account helpers**
|
||||
|
||||
Move `resolveAccountCredentialMode`, `parsePositiveInt`, and `isTruthyFlag` into a single helper consumed by both Accounts and Tokens.
|
||||
|
||||
**Step 3: De-duplicate proxy test aliases**
|
||||
|
||||
Make `testProxy` / `proxyTest` and stream variants reuse the same implementation function.
|
||||
|
||||
**Step 4: Verify behavior**
|
||||
|
||||
Run: `npm test -- src/server/services/proxyInputFileResolver.test.ts src/web/pages/helpers/accountConnection.test.ts src/web/api.test.ts`
|
||||
Expected: PASS
|
||||
|
||||
### Task 9: PR1 Verification And Commit
|
||||
|
||||
**Files:**
|
||||
- Verify: `scripts/dev/copy-runtime-db-generated.ts`
|
||||
- Verify: `scripts/dev/copy-runtime-db-generated.test.ts`
|
||||
- Verify: all files touched in Tasks 2-8
|
||||
|
||||
**Step 1: Run focused regression suite**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
npm test -- scripts/dev/copy-runtime-db-generated.test.ts src/server/proxy-core/cliProfiles/codexProfile.test.ts src/server/proxy-core/providers/headerUtils.test.ts src/server/proxy-core/surfaces/sharedSurface.test.ts src/server/routes/api/oauth.test.ts src/server/services/accountMutationWorkflow.test.ts src/server/services/modelService.discovery.test.ts src/server/services/oauth/oauthAccount.test.ts src/server/services/platforms/standardApiProvider.test.ts src/server/services/platforms/llmUpstream.test.ts src/server/services/proxyInputFileResolver.test.ts src/server/transformers/openai/responses/conversion.test.ts src/shared/tokenRouteContract.test.ts src/shared/tokenRoutePatterns.test.ts src/web/api.test.ts src/web/pages/helpers/accountConnection.test.ts
|
||||
```
|
||||
|
||||
Expected: PASS
|
||||
|
||||
**Step 2: Run server build**
|
||||
|
||||
Run: `npm run build:server`
|
||||
Expected: PASS
|
||||
|
||||
**Step 3: Commit PR1 consolidation work**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git add docs/plans/2026-03-22-single-source-consolidation-pr1.md scripts/dev/copy-runtime-db-generated.test.ts scripts/dev/copy-runtime-db-generated.ts src/server/proxy-core/cliProfiles/codexProfile.ts src/server/proxy-core/cliProfiles/codexProfile.test.ts src/server/proxy-core/providers/headerUtils.ts src/server/proxy-core/providers/headerUtils.test.ts src/server/proxy-core/providers/claudeProviderProfile.ts src/server/proxy-core/providers/codexProviderProfile.ts src/server/proxy-core/providers/geminiCliProviderProfile.ts src/server/proxy-core/surfaces/chatSurface.ts src/server/proxy-core/surfaces/geminiSurface.ts src/server/proxy-core/surfaces/inputFilesSurface.ts src/server/proxy-core/surfaces/openAiResponsesSurface.ts src/server/proxy-core/surfaces/sharedSurface.ts src/server/proxy-core/surfaces/sharedSurface.test.ts src/server/routes/api/accountTokens.ts src/server/routes/api/accounts.ts src/server/routes/api/oauth.test.ts src/server/routes/api/tokens.ts src/server/routes/proxy/downstreamClientContext.ts src/server/routes/proxy/responsesWebsocket.ts src/server/routes/proxy/upstreamEndpoint.ts src/server/services/accountMutationWorkflow.ts src/server/services/accountMutationWorkflow.test.ts src/server/services/backupService.ts src/server/services/modelService.discovery.test.ts src/server/services/modelService.ts src/server/services/oauth/oauthAccount.ts src/server/services/oauth/oauthAccount.test.ts src/server/services/oauth/quota.ts src/server/services/oauth/service.ts src/server/services/platforms/claude.ts src/server/services/platforms/cliproxyapi.ts src/server/services/platforms/gemini.ts src/server/services/platforms/llmUpstream.test.ts src/server/services/platforms/openai.ts src/server/services/platforms/standardApiProvider.ts src/server/services/platforms/standardApiProvider.test.ts src/server/services/proxyInputFileResolver.ts src/server/services/proxyInputFileResolver.test.ts src/server/services/siteProxy.ts src/server/services/tokenRouter.ts src/server/transformers/anthropic/messages/inbound.ts src/server/transformers/openai/responses/compatibility.ts src/server/transformers/openai/responses/conversion.test.ts src/server/transformers/openai/responses/conversion.ts src/server/transformers/openai/responses/normalization.ts src/shared/tokenRouteContract.d.ts src/shared/tokenRouteContract.js src/shared/tokenRouteContract.test.ts src/shared/tokenRoutePatterns.d.ts src/shared/tokenRoutePatterns.js src/shared/tokenRoutePatterns.test.ts src/web/api.ts src/web/api.test.ts src/web/pages/Accounts.tsx src/web/pages/Settings.tsx src/web/pages/Tokens.tsx src/web/pages/helpers/accountConnection.ts src/web/pages/helpers/accountConnection.test.ts src/web/pages/helpers/routeListVisibility.ts src/web/pages/token-routes/types.ts src/web/pages/token-routes/utils.ts
|
||||
git commit -m "refactor: consolidate single-source routing and protocol helpers"
|
||||
```
|
||||
|
||||
### Deferred After PR1
|
||||
|
||||
**Keep out of this PR:**
|
||||
- `shared/chatFormatsCore.ts` breakup
|
||||
- Gemini `generate-content` module boundary cleanup
|
||||
- schema contract/introspection meta-rule unification
|
||||
- `legacySchemaCompat.ts` feature compatibility dedupe
|
||||
- mobile page scaffold extraction
|
||||
- large-file decomposition for `Settings.tsx`, `ModelTester.tsx`, `tokenRouter.ts`, `modelService.ts`
|
||||
Reference in New Issue
Block a user