@mupt-ai/dari-gateway 0.3.4 → 0.3.5
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 +3 -1
- package/dist/models/canonical.d.ts +2 -0
- package/dist/models/canonical.js +3 -0
- package/dist/models/canonical_model_ids.json +33 -0
- package/dist/models/runtime_overrides.js +14 -0
- package/dist/self_host/config.d.ts +7 -1
- package/dist/self_host/config.js +30 -1
- package/dist/self_host/main.d.ts +4 -1
- package/dist/self_host/main.js +31 -4
- package/package.json +2 -1
- package/src/models/canonical.ts +4 -0
- package/src/models/canonical_model_ids.json +33 -0
- package/src/models/runtime_overrides.ts +14 -0
- package/src/self_host/config.ts +32 -2
- package/src/self_host/main.ts +30 -4
package/README.md
CHANGED
|
@@ -36,7 +36,9 @@ The gateway is the same runtime that serves Dari's hosted routing service. Choos
|
|
|
36
36
|
|
|
37
37
|
## Run
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
The easiest way to run it on your own machine is the [Dari CLI](https://github.com/mupt-ai/dari-cli): choose **Fully local** in `dari gateway setup` (or on your first `dari claude`), and it installs this gateway's standalone binary, runs it in the background, and connects your agents.
|
|
40
|
+
|
|
41
|
+
To run it yourself, use [Bun](https://bun.sh) 1.3 or later, or the standalone binary for your platform from the [releases](https://github.com/mupt-ai/dari-gateway/releases). A config file is optional: without `DARI_GATEWAY_CONFIG` or a `dari-gateway.yaml` in the working directory, the gateway serves a default router of current Anthropic and OpenAI models, each once its provider has a subscription or API key. To choose models, write a `dari-gateway.yaml` (start from [`dari-gateway.example.yaml`](dari-gateway.example.yaml)). Then:
|
|
40
42
|
|
|
41
43
|
```bash
|
|
42
44
|
export DARI_GATEWAY_KEYS="$(openssl rand -hex 24)" # keys your clients send
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[
|
|
2
|
+
"openai/gpt-6-astra",
|
|
3
|
+
"openai/gpt-6.1-terra",
|
|
4
|
+
"openai/gpt-6.1-sol",
|
|
5
|
+
"openai/gpt-6-sol",
|
|
6
|
+
"openai/gpt-6-luna",
|
|
7
|
+
"openai/gpt-5.6-sol",
|
|
8
|
+
"openai/gpt-5.6-terra",
|
|
9
|
+
"openai/gpt-5.6-luna",
|
|
10
|
+
"anthropic/claude-opus-5-5",
|
|
11
|
+
"anthropic/claude-opus-5",
|
|
12
|
+
"anthropic/claude-sonnet-5-5",
|
|
13
|
+
"anthropic/claude-sonnet-5",
|
|
14
|
+
"anthropic/claude-fable-5-2",
|
|
15
|
+
"anthropic/claude-fable-5-1",
|
|
16
|
+
"anthropic/claude-fable-5",
|
|
17
|
+
"anthropic/claude-haiku-5-5",
|
|
18
|
+
"anthropic/claude-haiku-4-5",
|
|
19
|
+
"meta/muse-wonder-1.0",
|
|
20
|
+
"meta/muse-spark-1.3",
|
|
21
|
+
"meta/muse-ember",
|
|
22
|
+
"meta/muse-spark-1.2",
|
|
23
|
+
"moonshotai/Kimi-K3",
|
|
24
|
+
"zai-org/GLM-5.3",
|
|
25
|
+
"zai-org/GLM-5.3-Flash",
|
|
26
|
+
"deepseek-ai/DeepSeek-V4-Pro-0813",
|
|
27
|
+
"zai-org/GLM-5.2",
|
|
28
|
+
"deepseek-ai/DeepSeek-V4-Flash-0731",
|
|
29
|
+
"deepseek-ai/DeepSeek-V4.1-Flash-1017",
|
|
30
|
+
"deepseek-ai/DeepSeek-V4.1-Flash",
|
|
31
|
+
"xai/grok-4.6",
|
|
32
|
+
"xai/grok-4.7"
|
|
33
|
+
]
|
|
@@ -26,6 +26,10 @@ const MODEL_COMPAT_OVERRIDES = {
|
|
|
26
26
|
forceAdaptiveThinking: true,
|
|
27
27
|
supportsTemperature: false,
|
|
28
28
|
},
|
|
29
|
+
"anthropic/claude-fable-5-2": {
|
|
30
|
+
forceAdaptiveThinking: true,
|
|
31
|
+
supportsTemperature: false,
|
|
32
|
+
},
|
|
29
33
|
"anthropic/claude-opus-5-5": {
|
|
30
34
|
forceAdaptiveThinking: true,
|
|
31
35
|
supportsTemperature: false,
|
|
@@ -65,6 +69,14 @@ const MODEL_COMPAT_OVERRIDES = {
|
|
|
65
69
|
supportsExplicitPromptCacheMode: true,
|
|
66
70
|
supportsReasoningConfigurationUpdate: true,
|
|
67
71
|
},
|
|
72
|
+
"openai/gpt-6.1-terra": {
|
|
73
|
+
supportsStrictMode: true,
|
|
74
|
+
supportsOpenAIGrammarTools: true,
|
|
75
|
+
supportsAdditionalTools: true,
|
|
76
|
+
supportsToolSearch: true,
|
|
77
|
+
supportsExplicitPromptCacheMode: true,
|
|
78
|
+
supportsReasoningConfigurationUpdate: true,
|
|
79
|
+
},
|
|
68
80
|
"openai/gpt-6-sol": {
|
|
69
81
|
supportsStrictMode: true,
|
|
70
82
|
supportsOpenAIGrammarTools: true,
|
|
@@ -138,7 +150,9 @@ const NATIVE_REASONING_COMPAT_KEYS = new Set([
|
|
|
138
150
|
const MODEL_INPUT_OVERRIDES = {
|
|
139
151
|
"anthropic/claude-haiku-5-5": ["text", "image"],
|
|
140
152
|
"anthropic/claude-sonnet-5-5": ["text", "image"],
|
|
153
|
+
"meta/muse-ember": ["text", "image"],
|
|
141
154
|
"openai/gpt-6.1-sol": ["text", "image"],
|
|
155
|
+
"openai/gpt-6.1-terra": ["text", "image"],
|
|
142
156
|
"openai/gpt-6-sol": ["text", "image"],
|
|
143
157
|
"openai/gpt-6-luna": ["text", "image"],
|
|
144
158
|
"openai/gpt-5.6-luna": ["text", "image"],
|
|
@@ -41,5 +41,11 @@ export type RouterModelConfig = {
|
|
|
41
41
|
id: string;
|
|
42
42
|
thinking?: ReasoningEffort[];
|
|
43
43
|
};
|
|
44
|
-
export declare
|
|
44
|
+
export declare const DEFAULT_GATEWAY_CONFIG: GatewayConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Load the config at `path`, which must exist. Without a path, load
|
|
47
|
+
* `dari-gateway.yaml` from the working directory when present, and otherwise
|
|
48
|
+
* serve DEFAULT_GATEWAY_CONFIG.
|
|
49
|
+
*/
|
|
50
|
+
export declare function loadGatewayConfig(path: string | undefined): Promise<GatewayConfig>;
|
|
45
51
|
export declare function parseGatewayConfig(raw: unknown): GatewayConfig;
|
package/dist/self_host/config.js
CHANGED
|
@@ -2,11 +2,40 @@ import { isReasoningEffort, isRecord, } from "@mupt-ai/dari-router/policy-engine
|
|
|
2
2
|
const ROUTER_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
3
3
|
const PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9.-]*$/;
|
|
4
4
|
const MODEL_ID_PATTERN = /^[a-z0-9][a-z0-9.-]*\/\S+$/;
|
|
5
|
+
// Served when no config file is set or found. A model is served once its
|
|
6
|
+
// provider has a credential (a connected subscription or the provider's API
|
|
7
|
+
// key); dari/routing without Auto Routing serves the first available one.
|
|
8
|
+
export const DEFAULT_GATEWAY_CONFIG = parseGatewayConfig({
|
|
9
|
+
routers: {
|
|
10
|
+
default: {
|
|
11
|
+
models: [
|
|
12
|
+
"anthropic/claude-sonnet-5-5",
|
|
13
|
+
"anthropic/claude-opus-5-5",
|
|
14
|
+
"anthropic/claude-fable-5-2",
|
|
15
|
+
"anthropic/claude-haiku-5-5",
|
|
16
|
+
"openai/gpt-6.1-sol",
|
|
17
|
+
"openai/gpt-6.1-terra",
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const DEFAULT_CONFIG_PATH = "dari-gateway.yaml";
|
|
23
|
+
/**
|
|
24
|
+
* Load the config at `path`, which must exist. Without a path, load
|
|
25
|
+
* `dari-gateway.yaml` from the working directory when present, and otherwise
|
|
26
|
+
* serve DEFAULT_GATEWAY_CONFIG.
|
|
27
|
+
*/
|
|
5
28
|
export async function loadGatewayConfig(path) {
|
|
29
|
+
if (path === undefined) {
|
|
30
|
+
if (!(await Bun.file(DEFAULT_CONFIG_PATH).exists()))
|
|
31
|
+
return DEFAULT_GATEWAY_CONFIG;
|
|
32
|
+
path = DEFAULT_CONFIG_PATH;
|
|
33
|
+
}
|
|
6
34
|
const file = Bun.file(path);
|
|
7
35
|
if (!(await file.exists())) {
|
|
8
36
|
throw new Error(`Gateway config not found at ${path}. Write one from dari-gateway.example.yaml, `
|
|
9
|
-
+ "or
|
|
37
|
+
+ "or unset DARI_GATEWAY_CONFIG to serve the default router "
|
|
38
|
+
+ "(in the container image, mount it at /config/dari-gateway.yaml).");
|
|
10
39
|
}
|
|
11
40
|
const text = await file.text();
|
|
12
41
|
const raw = path.endsWith(".json") ? JSON.parse(text) : Bun.YAML.parse(text);
|
package/dist/self_host/main.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Start a self-hosted Dari Gateway from environment variables:
|
|
4
4
|
*
|
|
5
5
|
* - `DARI_GATEWAY_KEYS` (required): comma-separated bearer keys clients use.
|
|
6
|
-
* - `DARI_GATEWAY_CONFIG`: router config file
|
|
6
|
+
* - `DARI_GATEWAY_CONFIG`: router config file. Unset, `dari-gateway.yaml`
|
|
7
|
+
* serves when present, and otherwise the default router.
|
|
7
8
|
* - `DARI_GATEWAY_DB`: SQLite database for subscriptions and prefix-cache
|
|
8
9
|
* state (default `dari-gateway.db`). It holds subscription tokens.
|
|
9
10
|
* - `DARI_AUTO_ROUTING_API_KEY`: choose among a router's models with Dari
|
|
@@ -15,3 +16,5 @@ export declare function startSelfHostedGateway(env?: Record<string, string>): Pr
|
|
|
15
16
|
server: import("../http/http2_server.js").Http2RoutingServer | Bun.Server<undefined>;
|
|
16
17
|
close: () => void;
|
|
17
18
|
}>;
|
|
19
|
+
/** Run the gateway as a process: the `dari-gateway` bin and compiled binary. */
|
|
20
|
+
export declare function runSelfHostedGateway(): Promise<void>;
|
package/dist/self_host/main.js
CHANGED
|
@@ -11,7 +11,8 @@ import { createPrioritySelectorBackend } from "../strategies/priority_selector.j
|
|
|
11
11
|
import { createSelfHostedPlatform } from "./platform.js";
|
|
12
12
|
import { providerForModel } from "@mupt-ai/dari-router/policy-engine";
|
|
13
13
|
import { checkProviderKey } from "../providers/key_check.js";
|
|
14
|
-
import {
|
|
14
|
+
import { CANONICAL_MODEL_IDS } from "../models/canonical.js";
|
|
15
|
+
import { DEFAULT_GATEWAY_CONFIG, loadGatewayConfig } from "./config.js";
|
|
15
16
|
import { configuredProviderApiKey, gatewayKeyIndex, providersWithoutCredentials } from "./resolve.js";
|
|
16
17
|
import { PrefixStore } from "./prefix_store.js";
|
|
17
18
|
import { SessionPinStore } from "./session_pin_store.js";
|
|
@@ -21,7 +22,8 @@ const SUBSCRIPTION_ROUTES = "/v1/organizations/current/credentials";
|
|
|
21
22
|
* Start a self-hosted Dari Gateway from environment variables:
|
|
22
23
|
*
|
|
23
24
|
* - `DARI_GATEWAY_KEYS` (required): comma-separated bearer keys clients use.
|
|
24
|
-
* - `DARI_GATEWAY_CONFIG`: router config file
|
|
25
|
+
* - `DARI_GATEWAY_CONFIG`: router config file. Unset, `dari-gateway.yaml`
|
|
26
|
+
* serves when present, and otherwise the default router.
|
|
25
27
|
* - `DARI_GATEWAY_DB`: SQLite database for subscriptions and prefix-cache
|
|
26
28
|
* state (default `dari-gateway.db`). It holds subscription tokens.
|
|
27
29
|
* - `DARI_AUTO_ROUTING_API_KEY`: choose among a router's models with Dari
|
|
@@ -37,7 +39,7 @@ export async function startSelfHostedGateway(env = process.env) {
|
|
|
37
39
|
if (gatewayKeys.length === 0) {
|
|
38
40
|
throw new Error("Set DARI_GATEWAY_KEYS to the comma-separated keys clients authenticate with.");
|
|
39
41
|
}
|
|
40
|
-
const config = await loadGatewayConfig(env.DARI_GATEWAY_CONFIG
|
|
42
|
+
const config = await loadGatewayConfig(env.DARI_GATEWAY_CONFIG?.trim() || undefined);
|
|
41
43
|
const databasePath = env.DARI_GATEWAY_DB ?? "dari-gateway.db";
|
|
42
44
|
const created = databasePath !== ":memory:" && !existsSync(databasePath);
|
|
43
45
|
const db = new Database(databasePath, { create: true });
|
|
@@ -134,7 +136,30 @@ async function checkConfiguredProviderKeys(config, providerApiKey) {
|
|
|
134
136
|
console.warn("dari-gateway could not check these provider API keys", JSON.stringify({ providers: unchecked }));
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
|
-
|
|
139
|
+
/** Run the gateway as a process: the `dari-gateway` bin and compiled binary. */
|
|
140
|
+
export async function runSelfHostedGateway() {
|
|
141
|
+
// `--print-config` prints the router config the gateway would serve, as
|
|
142
|
+
// JSON, and exits; `--print-default-config` prints the built-in default.
|
|
143
|
+
// The Dari CLI edits a local gateway's models from them.
|
|
144
|
+
if (process.argv.includes("--print-canonical-models")) {
|
|
145
|
+
console.log(JSON.stringify(CANONICAL_MODEL_IDS));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (process.argv.includes("--print-default-config")) {
|
|
149
|
+
console.log(JSON.stringify(DEFAULT_GATEWAY_CONFIG));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (process.argv.includes("--print-config")) {
|
|
153
|
+
try {
|
|
154
|
+
const config = await loadGatewayConfig(process.env.DARI_GATEWAY_CONFIG?.trim() || undefined);
|
|
155
|
+
console.log(JSON.stringify(config));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error(`dari-gateway: ${error instanceof Error ? error.message : String(error)}`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
138
163
|
// Startup errors are configuration mistakes: print the message, not a stack.
|
|
139
164
|
const started = await startSelfHostedGateway().catch((error) => {
|
|
140
165
|
console.error(`dari-gateway: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -142,3 +167,5 @@ if (import.meta.main) {
|
|
|
142
167
|
});
|
|
143
168
|
installShutdownHandlers(started.server, async () => started.close());
|
|
144
169
|
}
|
|
170
|
+
if (import.meta.main)
|
|
171
|
+
await runSelfHostedGateway();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mupt-ai/dari-gateway",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Self-hostable LLM gateway for coding agents and APIs: one endpoint for Anthropic Messages, OpenAI Chat Completions, and OpenAI Responses, with subscription pooling, failover, and prompt-cache-aware routing.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
],
|
|
43
43
|
"scripts": {
|
|
44
44
|
"start": "bun run src/self_host/main.ts",
|
|
45
|
+
"compile": "bun scripts/compile.ts",
|
|
45
46
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
46
47
|
"typecheck": "tsc --noEmit",
|
|
47
48
|
"test": "bun test",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[
|
|
2
|
+
"openai/gpt-6-astra",
|
|
3
|
+
"openai/gpt-6.1-terra",
|
|
4
|
+
"openai/gpt-6.1-sol",
|
|
5
|
+
"openai/gpt-6-sol",
|
|
6
|
+
"openai/gpt-6-luna",
|
|
7
|
+
"openai/gpt-5.6-sol",
|
|
8
|
+
"openai/gpt-5.6-terra",
|
|
9
|
+
"openai/gpt-5.6-luna",
|
|
10
|
+
"anthropic/claude-opus-5-5",
|
|
11
|
+
"anthropic/claude-opus-5",
|
|
12
|
+
"anthropic/claude-sonnet-5-5",
|
|
13
|
+
"anthropic/claude-sonnet-5",
|
|
14
|
+
"anthropic/claude-fable-5-2",
|
|
15
|
+
"anthropic/claude-fable-5-1",
|
|
16
|
+
"anthropic/claude-fable-5",
|
|
17
|
+
"anthropic/claude-haiku-5-5",
|
|
18
|
+
"anthropic/claude-haiku-4-5",
|
|
19
|
+
"meta/muse-wonder-1.0",
|
|
20
|
+
"meta/muse-spark-1.3",
|
|
21
|
+
"meta/muse-ember",
|
|
22
|
+
"meta/muse-spark-1.2",
|
|
23
|
+
"moonshotai/Kimi-K3",
|
|
24
|
+
"zai-org/GLM-5.3",
|
|
25
|
+
"zai-org/GLM-5.3-Flash",
|
|
26
|
+
"deepseek-ai/DeepSeek-V4-Pro-0813",
|
|
27
|
+
"zai-org/GLM-5.2",
|
|
28
|
+
"deepseek-ai/DeepSeek-V4-Flash-0731",
|
|
29
|
+
"deepseek-ai/DeepSeek-V4.1-Flash-1017",
|
|
30
|
+
"deepseek-ai/DeepSeek-V4.1-Flash",
|
|
31
|
+
"xai/grok-4.6",
|
|
32
|
+
"xai/grok-4.7"
|
|
33
|
+
]
|
|
@@ -29,6 +29,10 @@ const MODEL_COMPAT_OVERRIDES: Record<string, Record<string, unknown>> = {
|
|
|
29
29
|
forceAdaptiveThinking: true,
|
|
30
30
|
supportsTemperature: false,
|
|
31
31
|
},
|
|
32
|
+
"anthropic/claude-fable-5-2": {
|
|
33
|
+
forceAdaptiveThinking: true,
|
|
34
|
+
supportsTemperature: false,
|
|
35
|
+
},
|
|
32
36
|
"anthropic/claude-opus-5-5": {
|
|
33
37
|
forceAdaptiveThinking: true,
|
|
34
38
|
supportsTemperature: false,
|
|
@@ -68,6 +72,14 @@ const MODEL_COMPAT_OVERRIDES: Record<string, Record<string, unknown>> = {
|
|
|
68
72
|
supportsExplicitPromptCacheMode: true,
|
|
69
73
|
supportsReasoningConfigurationUpdate: true,
|
|
70
74
|
},
|
|
75
|
+
"openai/gpt-6.1-terra": {
|
|
76
|
+
supportsStrictMode: true,
|
|
77
|
+
supportsOpenAIGrammarTools: true,
|
|
78
|
+
supportsAdditionalTools: true,
|
|
79
|
+
supportsToolSearch: true,
|
|
80
|
+
supportsExplicitPromptCacheMode: true,
|
|
81
|
+
supportsReasoningConfigurationUpdate: true,
|
|
82
|
+
},
|
|
71
83
|
"openai/gpt-6-sol": {
|
|
72
84
|
supportsStrictMode: true,
|
|
73
85
|
supportsOpenAIGrammarTools: true,
|
|
@@ -144,7 +156,9 @@ const NATIVE_REASONING_COMPAT_KEYS: ReadonlySet<string> = new Set([
|
|
|
144
156
|
const MODEL_INPUT_OVERRIDES: Record<string, string[]> = {
|
|
145
157
|
"anthropic/claude-haiku-5-5": ["text", "image"],
|
|
146
158
|
"anthropic/claude-sonnet-5-5": ["text", "image"],
|
|
159
|
+
"meta/muse-ember": ["text", "image"],
|
|
147
160
|
"openai/gpt-6.1-sol": ["text", "image"],
|
|
161
|
+
"openai/gpt-6.1-terra": ["text", "image"],
|
|
148
162
|
"openai/gpt-6-sol": ["text", "image"],
|
|
149
163
|
"openai/gpt-6-luna": ["text", "image"],
|
|
150
164
|
"openai/gpt-5.6-luna": ["text", "image"],
|
package/src/self_host/config.ts
CHANGED
|
@@ -62,12 +62,42 @@ const ROUTER_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
|
62
62
|
const PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9.-]*$/;
|
|
63
63
|
const MODEL_ID_PATTERN = /^[a-z0-9][a-z0-9.-]*\/\S+$/;
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
// Served when no config file is set or found. A model is served once its
|
|
66
|
+
// provider has a credential (a connected subscription or the provider's API
|
|
67
|
+
// key); dari/routing without Auto Routing serves the first available one.
|
|
68
|
+
export const DEFAULT_GATEWAY_CONFIG: GatewayConfig = parseGatewayConfig({
|
|
69
|
+
routers: {
|
|
70
|
+
default: {
|
|
71
|
+
models: [
|
|
72
|
+
"anthropic/claude-sonnet-5-5",
|
|
73
|
+
"anthropic/claude-opus-5-5",
|
|
74
|
+
"anthropic/claude-fable-5-2",
|
|
75
|
+
"anthropic/claude-haiku-5-5",
|
|
76
|
+
"openai/gpt-6.1-sol",
|
|
77
|
+
"openai/gpt-6.1-terra",
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const DEFAULT_CONFIG_PATH = "dari-gateway.yaml";
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Load the config at `path`, which must exist. Without a path, load
|
|
87
|
+
* `dari-gateway.yaml` from the working directory when present, and otherwise
|
|
88
|
+
* serve DEFAULT_GATEWAY_CONFIG.
|
|
89
|
+
*/
|
|
90
|
+
export async function loadGatewayConfig(path: string | undefined): Promise<GatewayConfig> {
|
|
91
|
+
if (path === undefined) {
|
|
92
|
+
if (!(await Bun.file(DEFAULT_CONFIG_PATH).exists())) return DEFAULT_GATEWAY_CONFIG;
|
|
93
|
+
path = DEFAULT_CONFIG_PATH;
|
|
94
|
+
}
|
|
66
95
|
const file = Bun.file(path);
|
|
67
96
|
if (!(await file.exists())) {
|
|
68
97
|
throw new Error(
|
|
69
98
|
`Gateway config not found at ${path}. Write one from dari-gateway.example.yaml, `
|
|
70
|
-
+ "or
|
|
99
|
+
+ "or unset DARI_GATEWAY_CONFIG to serve the default router "
|
|
100
|
+
+ "(in the container image, mount it at /config/dari-gateway.yaml).",
|
|
71
101
|
);
|
|
72
102
|
}
|
|
73
103
|
const text = await file.text();
|
package/src/self_host/main.ts
CHANGED
|
@@ -13,7 +13,8 @@ import type { SelectorBackend } from "../strategies/routing.js";
|
|
|
13
13
|
import { createSelfHostedPlatform } from "./platform.js";
|
|
14
14
|
import { providerForModel } from "@mupt-ai/dari-router/policy-engine";
|
|
15
15
|
import { checkProviderKey } from "../providers/key_check.js";
|
|
16
|
-
import {
|
|
16
|
+
import { CANONICAL_MODEL_IDS } from "../models/canonical.js";
|
|
17
|
+
import { DEFAULT_GATEWAY_CONFIG, loadGatewayConfig, type GatewayConfig } from "./config.js";
|
|
17
18
|
import { configuredProviderApiKey, gatewayKeyIndex, providersWithoutCredentials } from "./resolve.js";
|
|
18
19
|
import { PrefixStore } from "./prefix_store.js";
|
|
19
20
|
import { SessionPinStore } from "./session_pin_store.js";
|
|
@@ -25,7 +26,8 @@ const SUBSCRIPTION_ROUTES = "/v1/organizations/current/credentials";
|
|
|
25
26
|
* Start a self-hosted Dari Gateway from environment variables:
|
|
26
27
|
*
|
|
27
28
|
* - `DARI_GATEWAY_KEYS` (required): comma-separated bearer keys clients use.
|
|
28
|
-
* - `DARI_GATEWAY_CONFIG`: router config file
|
|
29
|
+
* - `DARI_GATEWAY_CONFIG`: router config file. Unset, `dari-gateway.yaml`
|
|
30
|
+
* serves when present, and otherwise the default router.
|
|
29
31
|
* - `DARI_GATEWAY_DB`: SQLite database for subscriptions and prefix-cache
|
|
30
32
|
* state (default `dari-gateway.db`). It holds subscription tokens.
|
|
31
33
|
* - `DARI_AUTO_ROUTING_API_KEY`: choose among a router's models with Dari
|
|
@@ -41,7 +43,7 @@ export async function startSelfHostedGateway(env: Record<string, string> = proce
|
|
|
41
43
|
if (gatewayKeys.length === 0) {
|
|
42
44
|
throw new Error("Set DARI_GATEWAY_KEYS to the comma-separated keys clients authenticate with.");
|
|
43
45
|
}
|
|
44
|
-
const config = await loadGatewayConfig(env.DARI_GATEWAY_CONFIG
|
|
46
|
+
const config = await loadGatewayConfig(env.DARI_GATEWAY_CONFIG?.trim() || undefined);
|
|
45
47
|
const databasePath = env.DARI_GATEWAY_DB ?? "dari-gateway.db";
|
|
46
48
|
const created = databasePath !== ":memory:" && !existsSync(databasePath);
|
|
47
49
|
const db = new Database(databasePath, { create: true });
|
|
@@ -153,7 +155,29 @@ async function checkConfiguredProviderKeys(
|
|
|
153
155
|
}
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
/** Run the gateway as a process: the `dari-gateway` bin and compiled binary. */
|
|
159
|
+
export async function runSelfHostedGateway(): Promise<void> {
|
|
160
|
+
// `--print-config` prints the router config the gateway would serve, as
|
|
161
|
+
// JSON, and exits; `--print-default-config` prints the built-in default.
|
|
162
|
+
// The Dari CLI edits a local gateway's models from them.
|
|
163
|
+
if (process.argv.includes("--print-canonical-models")) {
|
|
164
|
+
console.log(JSON.stringify(CANONICAL_MODEL_IDS));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (process.argv.includes("--print-default-config")) {
|
|
168
|
+
console.log(JSON.stringify(DEFAULT_GATEWAY_CONFIG));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (process.argv.includes("--print-config")) {
|
|
172
|
+
try {
|
|
173
|
+
const config = await loadGatewayConfig(process.env.DARI_GATEWAY_CONFIG?.trim() || undefined);
|
|
174
|
+
console.log(JSON.stringify(config));
|
|
175
|
+
return;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(`dari-gateway: ${error instanceof Error ? error.message : String(error)}`);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
157
181
|
// Startup errors are configuration mistakes: print the message, not a stack.
|
|
158
182
|
const started = await startSelfHostedGateway().catch((error: unknown) => {
|
|
159
183
|
console.error(`dari-gateway: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -161,3 +185,5 @@ if (import.meta.main) {
|
|
|
161
185
|
});
|
|
162
186
|
installShutdownHandlers(started.server, async () => started.close());
|
|
163
187
|
}
|
|
188
|
+
|
|
189
|
+
if (import.meta.main) await runSelfHostedGateway();
|