@sunflower0305/claude-proxy 1.1.0 → 1.1.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/.env.example CHANGED
@@ -1,5 +1,5 @@
1
1
  # Choose your provider: qwen | deepseek | glm | minimax | kimi
2
- PROVIDER=qwen
2
+ PROVIDER=deepseek
3
3
 
4
4
  # Proxy server port (default: 8080)
5
5
  PROXY_PORT=8080
@@ -13,7 +13,7 @@ KIMI_API_KEY=your-kimi-key
13
13
 
14
14
  # Optional model overrides
15
15
  QWEN_MODEL=qwen-plus
16
- DEEPSEEK_MODEL=deepseek-chat
16
+ DEEPSEEK_MODEL=deepseek-v4-pro
17
17
  GLM_MODEL=glm-5.1
18
18
  MINIMAX_MODEL=MiniMax-M2.7-highspeed
19
19
  KIMI_MODEL=kimi-k2.6
package/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # claude-proxy
2
2
 
3
3
  [![CI](https://github.com/sunflower0305/claude-proxy/actions/workflows/ci.yml/badge.svg)](https://github.com/sunflower0305/claude-proxy/actions/workflows/ci.yml)
4
+ [![CD](https://github.com/sunflower0305/claude-proxy/actions/workflows/cd.yml/badge.svg)](https://github.com/sunflower0305/claude-proxy/actions/workflows/cd.yml)
4
5
  [![Coverage Status](https://coveralls.io/repos/github/sunflower0305/claude-proxy/badge.svg?branch=master)](https://coveralls.io/github/sunflower0305/claude-proxy?branch=master)
5
6
  [![npm version](https://img.shields.io/npm/v/%40sunflower0305%2Fclaude-proxy)](https://www.npmjs.com/package/@sunflower0305/claude-proxy)
7
+ [![npm downloads](https://img.shields.io/npm/dm/%40sunflower0305%2Fclaude-proxy)](https://www.npmjs.com/package/@sunflower0305/claude-proxy)
8
+ [![GitHub stars](https://img.shields.io/github/stars/sunflower0305/claude-proxy?cacheSeconds=60)](https://github.com/sunflower0305/claude-proxy/stargazers)
6
9
  [![License](https://img.shields.io/github/license/sunflower0305/claude-proxy)](https://github.com/sunflower0305/claude-proxy/blob/master/LICENSE)
7
10
 
8
11
  `claude-proxy` is published on npm as `@sunflower0305/claude-proxy`. It is a lightweight Express proxy that lets Claude Code or the Claude Agent SDK talk to domestic Chinese LLM providers through Anthropic-compatible `/v1/messages` endpoints.
@@ -31,25 +34,35 @@ The proxy reads configuration from environment variables. You can export them in
31
34
  Example `.env`:
32
35
 
33
36
  ```dotenv
34
- PROVIDER=qwen
37
+ PROVIDER=deepseek
35
38
  PROXY_PORT=8080
36
- QWEN_API_KEY=your-qwen-api-key
37
- QWEN_MODEL=qwen-plus
39
+ DEEPSEEK_API_KEY=your-deepseek-api-key
40
+ DEEPSEEK_MODEL=deepseek-v4-pro
38
41
  ```
39
42
 
40
43
  Available variables:
41
44
 
42
- | Variable | Purpose |
43
- | --- | --- |
44
- | `PROVIDER` | Active provider. Defaults to `qwen`. |
45
- | `PROXY_PORT` | Local server port. Defaults to `8080`. |
46
- | `QWEN_API_KEY` | API key for Qwen. |
47
- | `DEEPSEEK_API_KEY` | API key for DeepSeek. |
48
- | `GLM_API_KEY` | API key for GLM. |
49
- | `MINIMAX_API_KEY` | API key for MiniMax. |
50
- | `KIMI_API_KEY` | API key for Kimi. |
45
+ | Variable | Purpose |
46
+ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
47
+ | `PROVIDER` | Active provider. Defaults to `deepseek`. |
48
+ | `PROXY_PORT` | Local server port. Defaults to `8080`. |
49
+ | `QWEN_API_KEY` | API key for Qwen. |
50
+ | `DEEPSEEK_API_KEY` | API key for DeepSeek. |
51
+ | `GLM_API_KEY` | API key for GLM. |
52
+ | `MINIMAX_API_KEY` | API key for MiniMax. |
53
+ | `KIMI_API_KEY` | API key for Kimi. |
51
54
  | `QWEN_ANTHROPIC_BASE_URL`, `DEEPSEEK_ANTHROPIC_BASE_URL`, `GLM_ANTHROPIC_BASE_URL`, `MINIMAX_ANTHROPIC_BASE_URL`, `KIMI_ANTHROPIC_BASE_URL` | Override the upstream Anthropic-compatible base URL for a provider. |
52
- | `QWEN_MODEL`, `DEEPSEEK_MODEL`, `GLM_MODEL`, `MINIMAX_MODEL`, `KIMI_MODEL` | Override the default upstream model for a provider. |
55
+ | `QWEN_MODEL`, `DEEPSEEK_MODEL`, `GLM_MODEL`, `MINIMAX_MODEL`, `KIMI_MODEL` | Override the default upstream model for a provider. |
56
+
57
+ Provider defaults:
58
+
59
+ | Provider | Model env | Default model |
60
+ | --- | --- | --- |
61
+ | **`deepseek` (default)** | `DEEPSEEK_MODEL` | **`deepseek-v4-pro`** |
62
+ | `qwen` | `QWEN_MODEL` | `qwen-plus` |
63
+ | `glm` | `GLM_MODEL` | `glm-5.1` |
64
+ | `minimax` | `MINIMAX_MODEL` | `MiniMax-M2.7-highspeed` |
65
+ | `kimi` | `KIMI_MODEL` | `kimi-k2.6` |
53
66
 
54
67
  You can use the bundled example as a starting point:
55
68
 
@@ -87,12 +100,12 @@ const client = new Anthropic({
87
100
 
88
101
  ## Runtime Endpoints
89
102
 
90
- | Method | Path | Description |
91
- | --- | --- | --- |
92
- | `POST` | `/v1/messages` | Main Anthropic Messages API proxy endpoint |
93
- | `GET` | `/v1/models` | Lists supported Claude-facing model ids |
94
- | `GET` | `/health` | Health check |
95
- | `GET` / `POST` | `/api/provider` | Read or switch the active provider |
103
+ | Method | Path | Description |
104
+ | -------------- | --------------- | ------------------------------------------ |
105
+ | `POST` | `/v1/messages` | Main Anthropic Messages API proxy endpoint |
106
+ | `GET` | `/v1/models` | Lists supported Claude-facing model ids |
107
+ | `GET` | `/health` | Health check |
108
+ | `GET` / `POST` | `/api/provider` | Read or switch the active provider |
96
109
 
97
110
  Health check:
98
111
 
@@ -105,7 +118,7 @@ Switch provider at runtime:
105
118
  ```bash
106
119
  curl -X POST http://localhost:8080/api/provider \
107
120
  -H "Content-Type: application/json" \
108
- -d '{"provider":"deepseek"}'
121
+ -d '{"provider":"qwen"}'
109
122
  ```
110
123
 
111
124
  ## Library Usage
@@ -119,24 +132,6 @@ const app = createApp();
119
132
  app.listen(8080);
120
133
  ```
121
134
 
122
- ## Release Verification
123
-
124
- `v1.0.0` was verified on April 15, 2026 after publishing `@sunflower0305/claude-proxy` to npm.
125
-
126
- Verified items:
127
-
128
- - `npm install @sunflower0305/claude-proxy` completed successfully in a clean temporary directory
129
- - the published `claude-proxy` CLI started correctly from the installed package
130
- - `GET /health` and `GET /v1/models` returned `200 OK`
131
- - end-to-end proxying against a local mock Anthropic-compatible upstream passed for both non-streaming and streaming `POST /v1/messages`
132
- - end-to-end proxying against the real Qwen Anthropic-compatible upstream passed for both non-streaming and streaming `POST /v1/messages`
133
-
134
- Observed behavior during verification:
135
-
136
- - model remapping worked as expected, including `claude-sonnet-4-6 -> qwen-plus`
137
- - the real Qwen verification returned a valid assistant response for both buffered JSON and SSE streaming modes
138
- - the published package included the expected CLI entrypoint, `dist/` build output, `README.md`, `LICENSE`, and `.env.example`
139
-
140
135
  ## Development
141
136
 
142
137
  From source:
@@ -146,33 +141,6 @@ npm install
146
141
  npm run dev
147
142
  ```
148
143
 
149
- ## CI And Releases
150
-
151
- GitHub Actions currently provides a CI baseline only:
152
-
153
- - install dependencies with `pnpm`
154
- - run `npm run build`
155
- - run `npm run test:proxy-local`
156
- - run `npm run test:coverage`
157
- - upload `coverage/lcov.info` to Coveralls without blocking the workflow if the upload service is temporarily unavailable
158
-
159
- Publishing to npm remains a manual step. The package still relies on `prepack` and `prepublishOnly` in `package.json` to build and verify the artifact before release.
160
-
161
- Build and local package verification:
162
-
163
- ```bash
164
- npm run build
165
- env npm_config_cache=/tmp/claude-proxy-npm-cache npm pack --dry-run
166
- ```
167
-
168
- Local integration test:
169
-
170
- ```bash
171
- npm run test:proxy-local
172
- ```
173
-
174
- Release notes for `v1.0.0` are available in [docs/releases/1.0.0.md](/Users/joe/ai/claude-proxy/docs/releases/1.0.0.md).
175
-
176
144
  ## License
177
145
 
178
146
  MIT
package/dist/proxy.js CHANGED
@@ -41,7 +41,7 @@ const PROVIDERS = {
41
41
  baseUrl: pickEnv("DEEPSEEK_ANTHROPIC_BASE_URL") ||
42
42
  "https://api.deepseek.com/anthropic",
43
43
  apiKey: process.env.DEEPSEEK_API_KEY || "",
44
- model: pickEnv("DEEPSEEK_MODEL") || "deepseek-chat",
44
+ model: pickEnv("DEEPSEEK_MODEL") || "deepseek-v4-pro",
45
45
  },
46
46
  qwen: {
47
47
  baseUrl: pickEnv("QWEN_ANTHROPIC_BASE_URL") ||
@@ -71,10 +71,10 @@ function isProviderKey(value) {
71
71
  return Boolean(value && value in PROVIDERS);
72
72
  }
73
73
  function getInitialProvider() {
74
- return isProviderKey(process.env.PROVIDER) ? process.env.PROVIDER : "qwen";
74
+ return isProviderKey(process.env.PROVIDER) ? process.env.PROVIDER : "deepseek";
75
75
  }
76
76
  function getProviderConfig(provider) {
77
- return PROVIDERS[provider] || PROVIDERS.qwen;
77
+ return PROVIDERS[provider] || PROVIDERS.deepseek;
78
78
  }
79
79
  function getHeaderValue(value) {
80
80
  if (Array.isArray(value))
@@ -338,7 +338,7 @@ export function createApp() {
338
338
  app.get("/v1/models", (_req, res) => {
339
339
  res.json({
340
340
  data: [
341
- { id: "claude-opus-4-6", object: "model" },
341
+ { id: "claude-opus-4-7", object: "model" },
342
342
  { id: "claude-sonnet-4-6", object: "model" },
343
343
  { id: "claude-haiku-4-5", object: "model" },
344
344
  ],
@@ -406,13 +406,13 @@ if (isMainModule()) {
406
406
  app.listen(PORT, () => {
407
407
  console.log(`
408
408
  ╔═══════════════════════════════════════════════════════╗
409
- claude-proxy
409
+ claude-proxy
410
410
  ╠═══════════════════════════════════════════════════════╣
411
- ║ http://localhost:${PORT}
412
- ║ Backend: ${initialProvider} (${initialConfig.model})
411
+ ║ http://localhost:${PORT}
412
+ ║ Backend: ${initialProvider} (${initialConfig.model})
413
413
  ╠═══════════════════════════════════════════════════════╣
414
414
  ║ Set these env vars in your app: ║
415
- ║ ANTHROPIC_BASE_URL=http://localhost:${PORT}
415
+ ║ ANTHROPIC_BASE_URL=http://localhost:${PORT}
416
416
  ║ ANTHROPIC_API_KEY=any-string-works ║
417
417
  ╚═══════════════════════════════════════════════════════╝
418
418
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunflower0305/claude-proxy",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "description": "A proxy that lets Claude Agent SDK use domestic Chinese LLMs (DeepSeek, Qwen, GLM, MiniMax) as backend",
6
6
  "license": "MIT",
package/dist/app.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import express from "express";
2
- import { type ProxyRuntime } from "./runtime";
3
- export declare function createApp(runtime?: ProxyRuntime): express.Express;
package/dist/app.js DELETED
@@ -1,79 +0,0 @@
1
- import cors from "cors";
2
- import express from "express";
3
- import { handleMessagesRequest } from "./messages";
4
- import { isProviderKey } from "./providers";
5
- import { createRuntime } from "./runtime";
6
- const SUPPORTED_MODELS = [
7
- { id: "claude-opus-4-6", object: "model" },
8
- { id: "claude-sonnet-4-6", object: "model" },
9
- { id: "claude-haiku-4-5", object: "model" },
10
- ];
11
- export function createApp(runtime = createRuntime()) {
12
- const app = express();
13
- app.use(cors());
14
- app.use(express.json({ limit: "50mb" }));
15
- app.get("/", (_req, res) => {
16
- const config = runtime.getCurrentConfig();
17
- res.json({
18
- name: "claude-proxy",
19
- status: "running",
20
- provider: runtime.getCurrentProvider(),
21
- model: config.model,
22
- endpoints: {
23
- messages: "POST /v1/messages",
24
- health: "GET /health",
25
- models: "GET /v1/models",
26
- provider: "GET|POST /api/provider",
27
- },
28
- });
29
- });
30
- app.post("/v1/messages", async (req, res) => {
31
- await handleMessagesRequest(req, res, runtime);
32
- });
33
- app.get("/health", (_req, res) => {
34
- const config = runtime.getCurrentConfig();
35
- res.json({
36
- status: "ok",
37
- provider: runtime.getCurrentProvider(),
38
- model: config.model,
39
- });
40
- });
41
- app.get("/v1/models", (_req, res) => {
42
- res.json({ data: SUPPORTED_MODELS });
43
- });
44
- app.get("/api/provider", (_req, res) => {
45
- const config = runtime.getCurrentConfig();
46
- res.json({
47
- provider: runtime.getCurrentProvider(),
48
- model: config.model,
49
- baseUrl: config.baseUrl,
50
- availableProviders: [...runtime.providerKeys],
51
- });
52
- });
53
- app.post("/api/provider", (req, res) => {
54
- const { provider, model } = (req.body ?? {});
55
- const targetProvider = provider ?? runtime.inferProviderFromModel(model);
56
- if (!isProviderKey(targetProvider)) {
57
- res.status(400).json({
58
- error: `Unknown provider: ${targetProvider}`,
59
- available: [...runtime.providerKeys],
60
- });
61
- return;
62
- }
63
- const targetConfig = runtime.getConfig(targetProvider);
64
- if (!targetConfig.apiKey) {
65
- res.status(400).json({
66
- error: `API key not set for: ${targetProvider}`,
67
- });
68
- return;
69
- }
70
- const switchResult = runtime.setCurrentProvider(targetProvider);
71
- console.log(`Provider: ${switchResult.previousProvider} -> ${switchResult.provider}`);
72
- res.json({
73
- success: true,
74
- provider: switchResult.provider,
75
- model: switchResult.config.model,
76
- });
77
- });
78
- return app;
79
- }
@@ -1,3 +0,0 @@
1
- import type express from "express";
2
- import type { ProxyRuntime } from "./runtime";
3
- export declare function handleMessagesRequest(req: express.Request, res: express.Response, runtime: ProxyRuntime): Promise<void>;
package/dist/messages.js DELETED
@@ -1,221 +0,0 @@
1
- import { Readable } from "node:stream";
2
- const DEFAULT_ANTHROPIC_VERSION = "2023-06-01";
3
- const HOP_BY_HOP_RESPONSE_HEADERS = new Set([
4
- "connection",
5
- "content-encoding",
6
- "content-length",
7
- "keep-alive",
8
- "proxy-authenticate",
9
- "proxy-authorization",
10
- "te",
11
- "trailer",
12
- "transfer-encoding",
13
- "upgrade",
14
- ]);
15
- function getHeaderValue(value) {
16
- if (Array.isArray(value))
17
- return value.join(",");
18
- return value;
19
- }
20
- function buildUpstreamHeaders(req, stream, apiKey) {
21
- const headers = {
22
- "content-type": "application/json",
23
- "x-api-key": apiKey,
24
- "anthropic-version": getHeaderValue(req.headers["anthropic-version"]) ||
25
- DEFAULT_ANTHROPIC_VERSION,
26
- accept: getHeaderValue(req.headers.accept) ||
27
- (stream ? "text/event-stream" : "application/json"),
28
- };
29
- const anthropicBeta = getHeaderValue(req.headers["anthropic-beta"]);
30
- if (anthropicBeta) {
31
- headers["anthropic-beta"] = anthropicBeta;
32
- }
33
- return headers;
34
- }
35
- function getUpstreamUrl(baseUrl) {
36
- return `${baseUrl.replace(/\/$/, "")}/v1/messages`;
37
- }
38
- function buildUpstreamBody(body, targetModel) {
39
- const normalized = typeof body === "object" && body !== null
40
- ? { ...body }
41
- : {};
42
- normalized.model = targetModel;
43
- return normalized;
44
- }
45
- function copyUpstreamHeaders(upstream, res) {
46
- for (const [key, value] of upstream.headers.entries()) {
47
- if (HOP_BY_HOP_RESPONSE_HEADERS.has(key.toLowerCase()))
48
- continue;
49
- res.setHeader(key, value);
50
- }
51
- }
52
- function createProxyError(message) {
53
- return {
54
- type: "error",
55
- error: {
56
- type: "internal_error",
57
- message,
58
- },
59
- };
60
- }
61
- function toErrorMessage(error) {
62
- if (error instanceof Error && error.message)
63
- return error.message;
64
- return String(error);
65
- }
66
- function logTimingEvent(trace, phase, extra = {}) {
67
- console.log(`[ProxyTiming] ${JSON.stringify({
68
- request_id: trace.requestId,
69
- provider: trace.provider,
70
- requested_model: trace.requestedModel,
71
- target_model: trace.targetModel,
72
- stream: trace.stream,
73
- phase,
74
- elapsed_ms: Date.now() - trace.startedAt,
75
- at: new Date().toISOString(),
76
- ...extra,
77
- })}`);
78
- }
79
- function prepareUpstreamRequest(req, runtime, stream) {
80
- const config = runtime.getCurrentConfig();
81
- const targetModel = runtime.getTargetModel(req.body?.model);
82
- return {
83
- apiKey: config.apiKey,
84
- providerModel: config.model,
85
- requestBody: buildUpstreamBody(req.body, targetModel),
86
- requestedModel: req.body?.model,
87
- targetModel,
88
- trace: runtime.createRequestTrace(req.body?.model, targetModel, stream),
89
- upstreamUrl: getUpstreamUrl(config.baseUrl),
90
- };
91
- }
92
- async function handleNonStreamingRequest(req, res, runtime) {
93
- const prepared = prepareUpstreamRequest(req, runtime, false);
94
- console.log(`\n[${new Date().toISOString()}] ${String(prepared.requestedModel || prepared.providerModel)} -> ${prepared.targetModel} (non-streaming)`);
95
- logTimingEvent(prepared.trace, "start");
96
- try {
97
- const upstream = await fetch(prepared.upstreamUrl, {
98
- method: "POST",
99
- headers: buildUpstreamHeaders(req, false, prepared.apiKey),
100
- body: JSON.stringify(prepared.requestBody),
101
- });
102
- logTimingEvent(prepared.trace, "upstream_headers", {
103
- status: upstream.status,
104
- content_type: upstream.headers.get("content-type") || "",
105
- });
106
- const payload = Buffer.from(await upstream.arrayBuffer());
107
- copyUpstreamHeaders(upstream, res);
108
- res.status(upstream.status).send(payload);
109
- logTimingEvent(prepared.trace, "completed", {
110
- status: upstream.status,
111
- bytes: payload.byteLength,
112
- });
113
- }
114
- catch (error) {
115
- const message = toErrorMessage(error);
116
- console.error("Request error:", error);
117
- logTimingEvent(prepared.trace, "error", { message });
118
- res.status(500).json(createProxyError(message));
119
- }
120
- }
121
- async function handleStreamingRequest(req, res, runtime) {
122
- const prepared = prepareUpstreamRequest(req, runtime, true);
123
- const abortController = new AbortController();
124
- let clientClosed = false;
125
- let streamCompleted = false;
126
- let sawFirstChunk = false;
127
- console.log(`\n[${new Date().toISOString()}] ${String(prepared.requestedModel || prepared.providerModel)} -> ${prepared.targetModel} (streaming)`);
128
- logTimingEvent(prepared.trace, "start");
129
- res.on("close", () => {
130
- if (streamCompleted)
131
- return;
132
- clientClosed = true;
133
- abortController.abort();
134
- logTimingEvent(prepared.trace, "client_aborted");
135
- });
136
- try {
137
- const upstream = await fetch(prepared.upstreamUrl, {
138
- method: "POST",
139
- headers: buildUpstreamHeaders(req, true, prepared.apiKey),
140
- body: JSON.stringify(prepared.requestBody),
141
- signal: abortController.signal,
142
- });
143
- logTimingEvent(prepared.trace, "upstream_headers", {
144
- status: upstream.status,
145
- content_type: upstream.headers.get("content-type") || "",
146
- });
147
- copyUpstreamHeaders(upstream, res);
148
- res.status(upstream.status);
149
- if (!upstream.body) {
150
- streamCompleted = true;
151
- res.end();
152
- logTimingEvent(prepared.trace, "completed", {
153
- status: upstream.status,
154
- bytes: 0,
155
- no_body: true,
156
- });
157
- return;
158
- }
159
- const upstreamStream = Readable.fromWeb(upstream.body);
160
- upstreamStream.on("data", (chunk) => {
161
- if (sawFirstChunk)
162
- return;
163
- sawFirstChunk = true;
164
- const chunkSize = Buffer.isBuffer(chunk)
165
- ? chunk.byteLength
166
- : Buffer.byteLength(String(chunk));
167
- logTimingEvent(prepared.trace, "first_chunk", {
168
- status: upstream.status,
169
- chunk_bytes: chunkSize,
170
- });
171
- });
172
- upstreamStream.on("error", (error) => {
173
- if (clientClosed)
174
- return;
175
- console.error("Upstream stream error:", error);
176
- logTimingEvent(prepared.trace, "error", {
177
- status: upstream.status,
178
- message: toErrorMessage(error),
179
- });
180
- if (!res.writableEnded)
181
- res.end();
182
- });
183
- upstreamStream.pipe(res);
184
- await new Promise((resolve, reject) => {
185
- upstreamStream.on("end", () => {
186
- streamCompleted = true;
187
- logTimingEvent(prepared.trace, "completed", {
188
- status: upstream.status,
189
- });
190
- resolve();
191
- });
192
- upstreamStream.on("error", reject);
193
- res.on("close", () => resolve());
194
- });
195
- }
196
- catch (error) {
197
- const wasAborted = error instanceof Error && error.name === "AbortError"
198
- ? true
199
- : abortController.signal.aborted;
200
- if (clientClosed || wasAborted) {
201
- console.warn("[Proxy] Client disconnected, streaming aborted");
202
- return;
203
- }
204
- const message = toErrorMessage(error);
205
- console.error("Request error:", error);
206
- logTimingEvent(prepared.trace, "error", { message });
207
- if (!res.headersSent) {
208
- res.status(500).json(createProxyError(message));
209
- return;
210
- }
211
- if (!res.writableEnded)
212
- res.end();
213
- }
214
- }
215
- export async function handleMessagesRequest(req, res, runtime) {
216
- if (req.body?.stream) {
217
- await handleStreamingRequest(req, res, runtime);
218
- return;
219
- }
220
- await handleNonStreamingRequest(req, res, runtime);
221
- }
@@ -1,11 +0,0 @@
1
- export interface ProviderConfig {
2
- baseUrl: string;
3
- apiKey: string;
4
- model: string;
5
- }
6
- export declare const PROVIDER_KEYS: readonly ["deepseek", "qwen", "glm", "minimax", "kimi"];
7
- export type ProviderKey = (typeof PROVIDER_KEYS)[number];
8
- export type ProviderMap = Record<ProviderKey, ProviderConfig>;
9
- export declare const DEFAULT_PROVIDER: ProviderKey;
10
- export declare function isProviderKey(value: string | undefined): value is ProviderKey;
11
- export declare function loadProviders(env?: NodeJS.ProcessEnv): ProviderMap;
package/dist/providers.js DELETED
@@ -1,53 +0,0 @@
1
- export const PROVIDER_KEYS = [
2
- "deepseek",
3
- "qwen",
4
- "glm",
5
- "minimax",
6
- "kimi",
7
- ];
8
- export const DEFAULT_PROVIDER = "qwen";
9
- function pickEnv(env, ...keys) {
10
- for (const key of keys) {
11
- const value = env[key]?.trim();
12
- if (value)
13
- return value;
14
- }
15
- return undefined;
16
- }
17
- export function isProviderKey(value) {
18
- return Boolean(value && PROVIDER_KEYS.includes(value));
19
- }
20
- export function loadProviders(env = process.env) {
21
- return {
22
- deepseek: {
23
- baseUrl: pickEnv(env, "DEEPSEEK_ANTHROPIC_BASE_URL") ||
24
- "https://api.deepseek.com/anthropic",
25
- apiKey: env.DEEPSEEK_API_KEY || "",
26
- model: pickEnv(env, "DEEPSEEK_MODEL") || "deepseek-chat",
27
- },
28
- qwen: {
29
- baseUrl: pickEnv(env, "QWEN_ANTHROPIC_BASE_URL") ||
30
- "https://dashscope.aliyuncs.com/apps/anthropic",
31
- apiKey: env.QWEN_API_KEY || "",
32
- model: pickEnv(env, "QWEN_MODEL") || "qwen-plus",
33
- },
34
- glm: {
35
- baseUrl: pickEnv(env, "GLM_ANTHROPIC_BASE_URL") ||
36
- "https://open.bigmodel.cn/api/anthropic",
37
- apiKey: env.GLM_API_KEY || "",
38
- model: pickEnv(env, "GLM_MODEL") || "glm-5",
39
- },
40
- minimax: {
41
- baseUrl: pickEnv(env, "MINIMAX_ANTHROPIC_BASE_URL") ||
42
- "https://api.minimaxi.com/anthropic",
43
- apiKey: env.MINIMAX_API_KEY || "",
44
- model: pickEnv(env, "MINIMAX_MODEL") || "MiniMax-M2.7-highspeed",
45
- },
46
- kimi: {
47
- baseUrl: pickEnv(env, "KIMI_ANTHROPIC_BASE_URL") ||
48
- "https://api.moonshot.cn/anthropic",
49
- apiKey: env.KIMI_API_KEY || "",
50
- model: pickEnv(env, "KIMI_MODEL") || "kimi-k2.5",
51
- },
52
- };
53
- }
package/dist/runtime.d.ts DELETED
@@ -1,27 +0,0 @@
1
- import { type ProviderConfig, type ProviderKey, type ProviderMap } from "./providers";
2
- export interface RequestTrace {
3
- requestId: string;
4
- provider: ProviderKey;
5
- requestedModel: string;
6
- targetModel: string;
7
- stream: boolean;
8
- startedAt: number;
9
- }
10
- export interface ProxyRuntime {
11
- readonly providerKeys: readonly ProviderKey[];
12
- getCurrentProvider(): ProviderKey;
13
- getCurrentConfig(): ProviderConfig;
14
- getConfig(provider?: ProviderKey): ProviderConfig;
15
- getTargetModel(requestedModel: unknown): string;
16
- inferProviderFromModel(model: string | undefined): ProviderKey | undefined;
17
- setCurrentProvider(provider: ProviderKey): {
18
- previousProvider: ProviderKey;
19
- provider: ProviderKey;
20
- config: ProviderConfig;
21
- };
22
- createRequestTrace(requestedModel: unknown, targetModel: string, stream: boolean): RequestTrace;
23
- getStartupWarning(): string | undefined;
24
- }
25
- export declare function resolveTargetModel(requestedModel: unknown, currentModel: string): string;
26
- export declare function inferProviderFromModel(model: string | undefined): ProviderKey | undefined;
27
- export declare function createRuntime(env?: NodeJS.ProcessEnv, providers?: ProviderMap): ProxyRuntime;
package/dist/runtime.js DELETED
@@ -1,83 +0,0 @@
1
- import { DEFAULT_PROVIDER, PROVIDER_KEYS, isProviderKey, loadProviders, } from "./providers";
2
- export function resolveTargetModel(requestedModel, currentModel) {
3
- if (typeof requestedModel !== "string" || !requestedModel) {
4
- return currentModel;
5
- }
6
- const normalizedModel = requestedModel.toLowerCase();
7
- if (normalizedModel === "opus" ||
8
- normalizedModel === "sonnet" ||
9
- normalizedModel === "haiku") {
10
- return currentModel;
11
- }
12
- if (normalizedModel.startsWith("claude-") &&
13
- (normalizedModel.includes("-opus") ||
14
- normalizedModel.includes("-sonnet") ||
15
- normalizedModel.includes("-haiku"))) {
16
- return currentModel;
17
- }
18
- return requestedModel;
19
- }
20
- export function inferProviderFromModel(model) {
21
- const normalizedModel = model?.toLowerCase();
22
- if (!normalizedModel)
23
- return undefined;
24
- if (normalizedModel.includes("kimi"))
25
- return "kimi";
26
- if (normalizedModel.includes("qwen"))
27
- return "qwen";
28
- if (normalizedModel.includes("deepseek"))
29
- return "deepseek";
30
- if (normalizedModel.includes("glm"))
31
- return "glm";
32
- if (normalizedModel.includes("minimax"))
33
- return "minimax";
34
- return undefined;
35
- }
36
- export function createRuntime(env = process.env, providers = loadProviders(env)) {
37
- let currentProvider = isProviderKey(env.PROVIDER)
38
- ? env.PROVIDER
39
- : DEFAULT_PROVIDER;
40
- let requestSequence = 0;
41
- function getConfig(provider = currentProvider) {
42
- return providers[provider] || providers[DEFAULT_PROVIDER];
43
- }
44
- return {
45
- providerKeys: PROVIDER_KEYS,
46
- getCurrentProvider() {
47
- return currentProvider;
48
- },
49
- getCurrentConfig() {
50
- return getConfig();
51
- },
52
- getConfig,
53
- getTargetModel(requestedModel) {
54
- return resolveTargetModel(requestedModel, getConfig().model);
55
- },
56
- inferProviderFromModel,
57
- setCurrentProvider(provider) {
58
- const previousProvider = currentProvider;
59
- currentProvider = provider;
60
- return {
61
- previousProvider,
62
- provider: currentProvider,
63
- config: getConfig(),
64
- };
65
- },
66
- createRequestTrace(requestedModel, targetModel, stream) {
67
- const config = getConfig();
68
- return {
69
- requestId: `req-${++requestSequence}`,
70
- provider: currentProvider,
71
- requestedModel: String(requestedModel || config.model),
72
- targetModel,
73
- stream,
74
- startedAt: Date.now(),
75
- };
76
- },
77
- getStartupWarning() {
78
- if (getConfig().apiKey)
79
- return undefined;
80
- return `Warning: API key not configured for provider: ${currentProvider}`;
81
- },
82
- };
83
- }