@awiki/dsh-plugin 0.2.3 → 0.2.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 +86 -18
- package/README.zh.md +71 -17
- package/THIRD_PARTY_NOTICES.md +8 -1
- package/cordis.patch.yml +13 -0
- package/lib/client.js +8223 -1939
- package/lib/client.js.map +1 -1
- package/lib/index.js +1487 -60
- package/lib/model-proxy-contract.js +89 -0
- package/lib/model-proxy.js +383 -0
- package/lib/provider.js +2 -1
- package/lib/sdk-adapter-DKgkyApB.mjs +669 -0
- package/lib/typert.host.js +470 -482
- package/lib/typert.remote-client.d.ts +15 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +469 -103
- package/lib/types/attachment-cache.d.ts +28 -0
- package/lib/types/attachment-cache.d.ts.map +1 -0
- package/lib/types/attachment-cache.js +194 -0
- package/lib/types/attachment-cache.js.map +1 -0
- package/lib/types/client/AwikiMail.d.ts +14 -0
- package/lib/types/client/AwikiMail.d.ts.map +1 -0
- package/lib/types/client/AwikiMail.js +260 -0
- package/lib/types/client/AwikiMail.js.map +1 -0
- package/lib/types/client/AwikiOnboarding.d.ts +22 -0
- package/lib/types/client/AwikiOnboarding.d.ts.map +1 -0
- package/lib/types/client/AwikiOnboarding.js +87 -0
- package/lib/types/client/AwikiOnboarding.js.map +1 -0
- package/lib/types/client/AwikiOverlay.d.ts +15 -0
- package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
- package/lib/types/client/AwikiOverlay.js +251 -20
- package/lib/types/client/AwikiOverlay.js.map +1 -1
- package/lib/types/client/AwikiSettingsSection.d.ts +20 -4
- package/lib/types/client/AwikiSettingsSection.d.ts.map +1 -1
- package/lib/types/client/AwikiSettingsSection.js +279 -23
- package/lib/types/client/AwikiSettingsSection.js.map +1 -1
- package/lib/types/client/RechargeComingSoonDialog.d.ts +11 -0
- package/lib/types/client/RechargeComingSoonDialog.d.ts.map +1 -0
- package/lib/types/client/RechargeComingSoonDialog.js +8 -0
- package/lib/types/client/RechargeComingSoonDialog.js.map +1 -0
- package/lib/types/client/controller.d.ts +82 -4
- package/lib/types/client/controller.d.ts.map +1 -1
- package/lib/types/client/controller.js +517 -74
- package/lib/types/client/controller.js.map +1 -1
- package/lib/types/client/image-cache.d.ts +20 -0
- package/lib/types/client/image-cache.d.ts.map +1 -0
- package/lib/types/client/image-cache.js +177 -0
- package/lib/types/client/image-cache.js.map +1 -0
- package/lib/types/client/index.d.ts +2 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +84 -41
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/model-account-settings-availability.d.ts +4 -0
- package/lib/types/client/model-account-settings-availability.d.ts.map +1 -0
- package/lib/types/client/model-account-settings-availability.js +4 -0
- package/lib/types/client/model-account-settings-availability.js.map +1 -0
- package/lib/types/client/model-availability-controller.d.ts +27 -0
- package/lib/types/client/model-availability-controller.d.ts.map +1 -0
- package/lib/types/client/model-availability-controller.js +111 -0
- package/lib/types/client/model-availability-controller.js.map +1 -0
- package/lib/types/client/model-proxy-controller.d.ts +41 -0
- package/lib/types/client/model-proxy-controller.d.ts.map +1 -0
- package/lib/types/client/model-proxy-controller.js +241 -0
- package/lib/types/client/model-proxy-controller.js.map +1 -0
- package/lib/types/client/recharge-availability.d.ts +6 -0
- package/lib/types/client/recharge-availability.d.ts.map +1 -0
- package/lib/types/client/recharge-availability.js +6 -0
- package/lib/types/client/recharge-availability.js.map +1 -0
- package/lib/types/client/settings-locales.d.ts +1 -1
- package/lib/types/client/settings-locales.d.ts.map +1 -1
- package/lib/types/client/settings-locales.js +180 -2
- package/lib/types/client/settings-locales.js.map +1 -1
- package/lib/types/client/slots.d.ts +15 -2
- package/lib/types/client/slots.d.ts.map +1 -1
- package/lib/types/index.d.ts +56 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +462 -31
- package/lib/types/index.js.map +1 -1
- package/lib/types/listener-state.d.ts +23 -0
- package/lib/types/listener-state.d.ts.map +1 -0
- package/lib/types/listener-state.js +124 -0
- package/lib/types/listener-state.js.map +1 -0
- package/lib/types/listener.d.ts +96 -0
- package/lib/types/listener.d.ts.map +1 -0
- package/lib/types/listener.js +575 -0
- package/lib/types/listener.js.map +1 -0
- package/lib/types/mail.d.ts +8 -0
- package/lib/types/mail.d.ts.map +1 -0
- package/lib/types/mail.js +75 -0
- package/lib/types/mail.js.map +1 -0
- package/lib/types/model-proxy-contract.d.ts +61 -0
- package/lib/types/model-proxy-contract.d.ts.map +1 -0
- package/lib/types/model-proxy-contract.js +129 -0
- package/lib/types/model-proxy-contract.js.map +1 -0
- package/lib/types/model-proxy.d.ts +14 -0
- package/lib/types/model-proxy.d.ts.map +1 -0
- package/lib/types/model-proxy.js +371 -0
- package/lib/types/model-proxy.js.map +1 -0
- package/lib/types/provider-api.d.ts +78 -2
- package/lib/types/provider-api.d.ts.map +1 -1
- package/lib/types/provider.d.ts.map +1 -1
- package/lib/types/provider.js +1 -0
- package/lib/types/provider.js.map +1 -1
- package/lib/types/sdk-adapter.d.ts +26 -2
- package/lib/types/sdk-adapter.d.ts.map +1 -1
- package/lib/types/sdk-adapter.js +380 -24
- package/lib/types/sdk-adapter.js.map +1 -1
- package/lib/types/settings-rpc.js +1 -1
- package/lib/types/settings-rpc.js.map +1 -1
- package/lib/types/tools.d.ts +10 -0
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/tools.js +98 -0
- package/lib/types/tools.js.map +1 -1
- package/lib/types/types.d.ts +103 -1
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/package.json +65 -40
- package/lib/sdk-adapter-DK6RqV-c.mjs +0 -355
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//#region lib/types/model-proxy-contract.js
|
|
2
|
+
/** Browser-safe contracts for the loopback AWiki-hosted DeepSeek proxy channel. */
|
|
3
|
+
const AWIKI_MODEL_PROXY_RPC_CHANNEL = "/awiki-model-proxy";
|
|
4
|
+
const AWIKI_MODEL_PROXY_RPC_ENDPOINTS = {
|
|
5
|
+
status: "status",
|
|
6
|
+
usage: "usage",
|
|
7
|
+
setEnabled: "set-enabled",
|
|
8
|
+
createRecharge: "create-recharge",
|
|
9
|
+
rechargeStatus: "recharge-status",
|
|
10
|
+
closeRecharge: "close-recharge"
|
|
11
|
+
};
|
|
12
|
+
function decodeModelProxyStatus(value) {
|
|
13
|
+
if (!isRecord(value) || typeof value.enabled !== "boolean") return void 0;
|
|
14
|
+
const account = decodeAccount(value.account);
|
|
15
|
+
if (account === void 0) return void 0;
|
|
16
|
+
const pendingRechargeOrder = value.pending_recharge_order === null ? null : decodeRechargeOrder(value.pending_recharge_order);
|
|
17
|
+
if (pendingRechargeOrder === void 0 || pendingRechargeOrder !== null && pendingRechargeOrder.status !== "pending") return void 0;
|
|
18
|
+
if (value.recommended_model !== "deepseek-v4-flash" || !Array.isArray(value.models) || value.models.length !== 2 || value.models[0] !== "deepseek-v4-flash" || value.models[1] !== "deepseek-v4-pro") return void 0;
|
|
19
|
+
return {
|
|
20
|
+
enabled: value.enabled,
|
|
21
|
+
account,
|
|
22
|
+
pending_recharge_order: pendingRechargeOrder,
|
|
23
|
+
recommended_model: "deepseek-v4-flash",
|
|
24
|
+
models: ["deepseek-v4-flash", "deepseek-v4-pro"]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function decodeModelProxyUsage(value) {
|
|
28
|
+
if (!Array.isArray(value)) return void 0;
|
|
29
|
+
const usage = value.map(decodeUsage);
|
|
30
|
+
return usage.every((item) => item !== void 0) ? usage : void 0;
|
|
31
|
+
}
|
|
32
|
+
function decodeRechargeOrder(value) {
|
|
33
|
+
if (!isRecord(value) || typeof value.out_trade_no !== "string" || !Number.isSafeInteger(value.amount_cents) || ![
|
|
34
|
+
"pending",
|
|
35
|
+
"paid",
|
|
36
|
+
"closed"
|
|
37
|
+
].includes(String(value.status)) || typeof value.provider !== "string" || typeof value.payment_method !== "string" || typeof value.created_at !== "string") return void 0;
|
|
38
|
+
if (value.payment_action !== void 0 && (!isRecord(value.payment_action) || !["redirect_url", "qr_code"].includes(String(value.payment_action.type)) || typeof value.payment_action.data !== "string")) return void 0;
|
|
39
|
+
const action = value.payment_action;
|
|
40
|
+
return {
|
|
41
|
+
out_trade_no: value.out_trade_no,
|
|
42
|
+
amount_cents: value.amount_cents,
|
|
43
|
+
status: value.status,
|
|
44
|
+
provider: value.provider,
|
|
45
|
+
payment_method: value.payment_method,
|
|
46
|
+
created_at: value.created_at,
|
|
47
|
+
...action === void 0 ? {} : { payment_action: {
|
|
48
|
+
type: action.type,
|
|
49
|
+
data: action.data
|
|
50
|
+
} }
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function decodeCloseRechargeResult(value) {
|
|
54
|
+
return isRecord(value) && value.closed === true ? { closed: true } : void 0;
|
|
55
|
+
}
|
|
56
|
+
function decodeAccount(value) {
|
|
57
|
+
if (!(isRecord(value) && typeof value.did === "string" && Number.isSafeInteger(value.balance_cents) && typeof value.balance === "string" && value.currency === "CNY" && typeof value.model_access_available === "boolean" && (value.model_access_reason === null || typeof value.model_access_reason === "string") && ["strict", "development_bypass"].includes(String(value.billing_mode)) && typeof value.payments_available === "boolean")) return void 0;
|
|
58
|
+
return {
|
|
59
|
+
did: value.did,
|
|
60
|
+
balance_cents: value.balance_cents,
|
|
61
|
+
balance: value.balance,
|
|
62
|
+
currency: "CNY",
|
|
63
|
+
model_access_available: value.model_access_available,
|
|
64
|
+
model_access_reason: value.model_access_reason,
|
|
65
|
+
billing_mode: value.billing_mode,
|
|
66
|
+
payments_available: value.payments_available
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function decodeUsage(value) {
|
|
70
|
+
if (!(isRecord(value) && Number.isSafeInteger(value.id) && typeof value.endpoint === "string" && typeof value.model === "string" && Number.isSafeInteger(value.cache_hit_tokens) && Number.isSafeInteger(value.cache_miss_tokens) && Number.isSafeInteger(value.completion_tokens) && ["strict", "development_bypass"].includes(String(value.billing_mode)) && (value.calculated_cost_micros === null || Number.isSafeInteger(value.calculated_cost_micros)) && Number.isSafeInteger(value.charged_micros) && typeof value.estimated === "boolean" && typeof value.created_at === "string")) return void 0;
|
|
71
|
+
return {
|
|
72
|
+
id: value.id,
|
|
73
|
+
endpoint: value.endpoint,
|
|
74
|
+
model: value.model,
|
|
75
|
+
cache_hit_tokens: value.cache_hit_tokens,
|
|
76
|
+
cache_miss_tokens: value.cache_miss_tokens,
|
|
77
|
+
completion_tokens: value.completion_tokens,
|
|
78
|
+
billing_mode: value.billing_mode,
|
|
79
|
+
calculated_cost_micros: value.calculated_cost_micros,
|
|
80
|
+
charged_micros: value.charged_micros,
|
|
81
|
+
estimated: value.estimated,
|
|
82
|
+
created_at: value.created_at
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function isRecord(value) {
|
|
86
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { AWIKI_MODEL_PROXY_RPC_CHANNEL, AWIKI_MODEL_PROXY_RPC_ENDPOINTS, decodeCloseRechargeResult, decodeModelProxyStatus, decodeModelProxyUsage, decodeRechargeOrder };
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { AWIKI_MODEL_PROXY_RPC_CHANNEL, AWIKI_MODEL_PROXY_RPC_ENDPOINTS, decodeModelProxyStatus, decodeModelProxyUsage, decodeRechargeOrder } from "./model-proxy-contract.js";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
4
|
+
import { LlmError, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
5
|
+
import { getOrCreateAnonymousUserId } from "@deepseek-ai/dsh-anonymous-user-id";
|
|
6
|
+
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
7
|
+
import { DeepSeekAdapter } from "@deepseek-ai/dsh-llm-deepseek";
|
|
8
|
+
//#region lib/types/model-proxy.js
|
|
9
|
+
/** Host-only AWiki-authenticated model-proxy provider and loopback account API. */
|
|
10
|
+
const name = "awiki-model-proxy";
|
|
11
|
+
const inject = [
|
|
12
|
+
"awiki",
|
|
13
|
+
"llm",
|
|
14
|
+
"settings",
|
|
15
|
+
"agentDefaultModel",
|
|
16
|
+
"connection"
|
|
17
|
+
];
|
|
18
|
+
const SETTINGS = settingsNamespace("awiki-model-proxy");
|
|
19
|
+
const PROVIDER = "awiki-deepseek";
|
|
20
|
+
const FLASH = "deepseek-v4-flash";
|
|
21
|
+
const PRO = "deepseek-v4-pro";
|
|
22
|
+
const MODELS = [FLASH, PRO];
|
|
23
|
+
const PROVIDER_NAME = "AWiki-hosted DeepSeek";
|
|
24
|
+
const SettingsSchema = z.object({
|
|
25
|
+
enabled: z.boolean().default(false),
|
|
26
|
+
previousProvider: z.string(),
|
|
27
|
+
previousModel: z.string(),
|
|
28
|
+
previousReasoningEffort: z.string()
|
|
29
|
+
});
|
|
30
|
+
const Config = z.object({
|
|
31
|
+
baseURL: z.string().default("https://model.awiki.info"),
|
|
32
|
+
contextWindow: z.number().step(1).min(1).default(1e6),
|
|
33
|
+
maxTokens: z.number().step(1).min(1).default(8192),
|
|
34
|
+
tokenRefreshSkewSeconds: z.number().step(1).min(0).default(60)
|
|
35
|
+
});
|
|
36
|
+
function apply(ctx, input = {}) {
|
|
37
|
+
const config = resolveConfig(input);
|
|
38
|
+
const settings = ctx.settings.register(SETTINGS, SettingsSchema, {
|
|
39
|
+
base: { enabled: false },
|
|
40
|
+
applies: "live"
|
|
41
|
+
});
|
|
42
|
+
const token = new ModelProxyToken(ctx, config);
|
|
43
|
+
const adapter = new AwikiHostedDeepSeekAdapter({
|
|
44
|
+
options: () => ({
|
|
45
|
+
baseURL: new URL("/v1", config.baseURL).toString().replace(/\/$/, ""),
|
|
46
|
+
apiKeyEnv: credentialRef("AWIKI_MODEL_PROXY_TOKEN"),
|
|
47
|
+
defaults: {},
|
|
48
|
+
maxTokens: config.maxTokens,
|
|
49
|
+
defaultContextWindow: config.contextWindow,
|
|
50
|
+
models: [{
|
|
51
|
+
id: FLASH,
|
|
52
|
+
name: "DeepSeek V4 Flash",
|
|
53
|
+
contextWindow: config.contextWindow,
|
|
54
|
+
maxTokens: config.maxTokens
|
|
55
|
+
}, {
|
|
56
|
+
id: PRO,
|
|
57
|
+
name: "DeepSeek V4 Pro",
|
|
58
|
+
contextWindow: config.contextWindow,
|
|
59
|
+
maxTokens: config.maxTokens
|
|
60
|
+
}],
|
|
61
|
+
streamIdleTimeoutMs: 3e5,
|
|
62
|
+
retryPolicy: resolveRetryPolicy(void 0, "awiki-model-proxy: retryPolicy")
|
|
63
|
+
}),
|
|
64
|
+
resolveApiKey: () => token.get(),
|
|
65
|
+
resolveUserId: () => getOrCreateAnonymousUserId()
|
|
66
|
+
});
|
|
67
|
+
let route;
|
|
68
|
+
let directory;
|
|
69
|
+
let sessionStatus;
|
|
70
|
+
let sessionRefresh;
|
|
71
|
+
const sync = () => {
|
|
72
|
+
const enabled = settings.get().enabled;
|
|
73
|
+
if (enabled && sessionStatus === "active" && route === void 0) {
|
|
74
|
+
directory = ctx.llm.registerConfigurableProviders([{
|
|
75
|
+
provider: PROVIDER,
|
|
76
|
+
displayName: PROVIDER_NAME,
|
|
77
|
+
settingsNs: SETTINGS,
|
|
78
|
+
settingsPath: []
|
|
79
|
+
}]);
|
|
80
|
+
route = ctx.llm.registerAdapter([PROVIDER], adapter);
|
|
81
|
+
} else if ((!enabled || sessionStatus !== "active") && route !== void 0) {
|
|
82
|
+
route();
|
|
83
|
+
directory?.();
|
|
84
|
+
route = void 0;
|
|
85
|
+
directory = void 0;
|
|
86
|
+
token.clear();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const publishSession = (session) => {
|
|
90
|
+
sessionStatus = session.status;
|
|
91
|
+
token.clear();
|
|
92
|
+
sync();
|
|
93
|
+
};
|
|
94
|
+
const refreshSession = () => {
|
|
95
|
+
if (sessionStatus !== void 0) return Promise.resolve(sessionStatus);
|
|
96
|
+
return sessionRefresh ??= ctx.awiki.getSession().then((result) => {
|
|
97
|
+
if (!result.ok) return void 0;
|
|
98
|
+
publishSession(result.value);
|
|
99
|
+
return result.value.status;
|
|
100
|
+
}).finally(() => {
|
|
101
|
+
sessionRefresh = void 0;
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
sync();
|
|
105
|
+
refreshSession();
|
|
106
|
+
ctx.on("awiki/session", (session) => {
|
|
107
|
+
publishSession(session);
|
|
108
|
+
});
|
|
109
|
+
ctx.on("settings/updated", (namespace) => {
|
|
110
|
+
if (namespace === SETTINGS) sync();
|
|
111
|
+
});
|
|
112
|
+
ctx.effect(() => () => {
|
|
113
|
+
route?.();
|
|
114
|
+
directory?.();
|
|
115
|
+
token.clear();
|
|
116
|
+
}, "awiki-model-proxy: release adapter and token");
|
|
117
|
+
const handler = createRpcHandler(ctx, config, token, () => settings.get(), sync, async () => await refreshSession() === "active");
|
|
118
|
+
ctx.connection.rpc.handle(AWIKI_MODEL_PROXY_RPC_CHANNEL, handler, { authority: "loopback" });
|
|
119
|
+
}
|
|
120
|
+
var ModelProxyToken = class {
|
|
121
|
+
ctx;
|
|
122
|
+
config;
|
|
123
|
+
value;
|
|
124
|
+
expiresAt = 0;
|
|
125
|
+
pending;
|
|
126
|
+
generation = 0;
|
|
127
|
+
constructor(ctx, config) {
|
|
128
|
+
this.ctx = ctx;
|
|
129
|
+
this.config = config;
|
|
130
|
+
}
|
|
131
|
+
get() {
|
|
132
|
+
if (this.value !== void 0 && Date.now() < this.expiresAt - this.config.tokenRefreshSkewMs) return Promise.resolve(this.value);
|
|
133
|
+
if (this.pending !== void 0) return this.pending;
|
|
134
|
+
const generation = this.generation;
|
|
135
|
+
const pending = this.refresh(generation).finally(() => {
|
|
136
|
+
if (this.pending === pending) this.pending = void 0;
|
|
137
|
+
});
|
|
138
|
+
this.pending = pending;
|
|
139
|
+
return pending;
|
|
140
|
+
}
|
|
141
|
+
clear() {
|
|
142
|
+
this.generation += 1;
|
|
143
|
+
this.value = void 0;
|
|
144
|
+
this.expiresAt = 0;
|
|
145
|
+
this.pending = void 0;
|
|
146
|
+
}
|
|
147
|
+
invalidate(value) {
|
|
148
|
+
if (this.value === value) this.clear();
|
|
149
|
+
}
|
|
150
|
+
async refresh(generation) {
|
|
151
|
+
const response = await this.ctx.awiki.externalHttpAuth.dispatch(new Request(new URL("/api/token", this.config.baseURL), { method: "POST" }), (request) => fetch(request));
|
|
152
|
+
if (!response.ok) throw await modelProxyError(response, "AWiki-hosted DeepSeek authentication failed");
|
|
153
|
+
const value = await response.json();
|
|
154
|
+
if (!isRecord(value) || typeof value.access_token !== "string" || value.access_token.length === 0 || !Number.isSafeInteger(value.expires_in) || value.expires_in <= 0) throw new LlmError("AWiki-hosted DeepSeek authentication returned an invalid response", "AUTH");
|
|
155
|
+
const token = value;
|
|
156
|
+
if (generation !== this.generation) throw new LlmError("AWiki-hosted DeepSeek authentication state changed", "AUTH");
|
|
157
|
+
this.value = token.access_token;
|
|
158
|
+
this.expiresAt = Date.now() + token.expires_in * 1e3;
|
|
159
|
+
return token.access_token;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var AwikiHostedDeepSeekAdapter = class extends DeepSeekAdapter {
|
|
163
|
+
providerInfo(provider) {
|
|
164
|
+
return {
|
|
165
|
+
id: provider,
|
|
166
|
+
name: PROVIDER_NAME
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
function createRpcHandler(ctx, config, token, currentSettings, sync, sessionActive) {
|
|
171
|
+
return async (endpoint, payload, signal) => {
|
|
172
|
+
try {
|
|
173
|
+
if (signal.aborted) throw new Error("request cancelled");
|
|
174
|
+
if (!await sessionActive()) throw new LlmError("Sign in to AWiki before using AWiki-hosted DeepSeek.", "AUTH");
|
|
175
|
+
if (endpoint === AWIKI_MODEL_PROXY_RPC_ENDPOINTS.status) return {
|
|
176
|
+
ok: true,
|
|
177
|
+
value: await status(config, token, currentSettings().enabled, signal)
|
|
178
|
+
};
|
|
179
|
+
if (endpoint === AWIKI_MODEL_PROXY_RPC_ENDPOINTS.usage) {
|
|
180
|
+
const value = await authenticatedJson(config, token, "/api/usage", { signal });
|
|
181
|
+
const usage = decodeModelProxyUsage(value);
|
|
182
|
+
if (usage === void 0) throw new Error("invalid usage response");
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
value: usage
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (endpoint === AWIKI_MODEL_PROXY_RPC_ENDPOINTS.createRecharge) {
|
|
189
|
+
if (!isRecord(payload) || !Number.isSafeInteger(payload.amount_cents)) return badRequest();
|
|
190
|
+
const value = await authenticatedJson(config, token, "/api/recharge/orders", {
|
|
191
|
+
method: "POST",
|
|
192
|
+
headers: {
|
|
193
|
+
"content-type": "application/json",
|
|
194
|
+
"idempotency-key": globalThis.crypto.randomUUID()
|
|
195
|
+
},
|
|
196
|
+
body: JSON.stringify({ amount_cents: payload.amount_cents }),
|
|
197
|
+
signal
|
|
198
|
+
});
|
|
199
|
+
const order = decodeRechargeOrder(value);
|
|
200
|
+
if (order === void 0 || order.payment_action === void 0) throw new Error("invalid recharge response");
|
|
201
|
+
return {
|
|
202
|
+
ok: true,
|
|
203
|
+
value: order
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (endpoint === AWIKI_MODEL_PROXY_RPC_ENDPOINTS.rechargeStatus) {
|
|
207
|
+
if (!isRecord(payload) || typeof payload.out_trade_no !== "string") return badRequest();
|
|
208
|
+
const value = await authenticatedJson(config, token, `/api/recharge/orders/${encodeURIComponent(payload.out_trade_no)}`, { signal });
|
|
209
|
+
const order = decodeRechargeOrder(value);
|
|
210
|
+
if (order === void 0) throw new Error("invalid recharge status response");
|
|
211
|
+
return {
|
|
212
|
+
ok: true,
|
|
213
|
+
value: order
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (endpoint === AWIKI_MODEL_PROXY_RPC_ENDPOINTS.closeRecharge) {
|
|
217
|
+
if (!isRecord(payload) || typeof payload.out_trade_no !== "string") return badRequest();
|
|
218
|
+
if ((await authenticatedResponse(config, token, `/api/recharge/orders/${encodeURIComponent(payload.out_trade_no)}/close`, {
|
|
219
|
+
method: "POST",
|
|
220
|
+
signal
|
|
221
|
+
})).status !== 204) throw new Error("invalid recharge close response");
|
|
222
|
+
return {
|
|
223
|
+
ok: true,
|
|
224
|
+
value: { closed: true }
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (endpoint === AWIKI_MODEL_PROXY_RPC_ENDPOINTS.setEnabled) {
|
|
228
|
+
if (!isRecord(payload) || typeof payload.enabled !== "boolean") return badRequest();
|
|
229
|
+
if (payload.enabled) {
|
|
230
|
+
if (!(await status(config, token, false, signal)).account.model_access_available) return modelUnavailable("Account balance is required before enabling AWiki-hosted DeepSeek.");
|
|
231
|
+
const previous = ctx.agentDefaultModel.currentSelection();
|
|
232
|
+
await ctx.settings.update(SETTINGS, {
|
|
233
|
+
enabled: true,
|
|
234
|
+
previousProvider: previous.provider,
|
|
235
|
+
previousModel: previous.model,
|
|
236
|
+
...previous.reasoningEffort === void 0 ? {} : { previousReasoningEffort: String(previous.reasoningEffort) }
|
|
237
|
+
});
|
|
238
|
+
sync();
|
|
239
|
+
await ctx.agentDefaultModel.saveSelection({
|
|
240
|
+
provider: PROVIDER,
|
|
241
|
+
model: FLASH
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
const stored = currentSettings();
|
|
245
|
+
await ctx.settings.update(SETTINGS, { enabled: false });
|
|
246
|
+
sync();
|
|
247
|
+
if (ctx.agentDefaultModel.currentSelection().provider === PROVIDER) await ctx.agentDefaultModel.saveSelection({
|
|
248
|
+
provider: stored.previousProvider ?? "deepseek-official",
|
|
249
|
+
model: stored.previousModel ?? FLASH,
|
|
250
|
+
...stored.previousReasoningEffort === void 0 ? {} : { reasoningEffort: stored.previousReasoningEffort }
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
ok: true,
|
|
255
|
+
value: await status(config, token, payload.enabled, signal)
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return badRequest();
|
|
259
|
+
} catch (error) {
|
|
260
|
+
ctx.logger.warn("awiki-model-proxy: loopback request failed");
|
|
261
|
+
ctx.logger.warn(error);
|
|
262
|
+
return internal(displayMessage(error));
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
async function status(config, token, enabled, signal) {
|
|
267
|
+
const [account, pendingRechargeOrder] = await Promise.all([authenticatedJson(config, token, "/api/account", { signal }), authenticatedJson(config, token, "/api/recharge/orders/pending", { signal })]);
|
|
268
|
+
const decoded = decodeModelProxyStatus({
|
|
269
|
+
enabled,
|
|
270
|
+
account,
|
|
271
|
+
pending_recharge_order: pendingRechargeOrder,
|
|
272
|
+
recommended_model: FLASH,
|
|
273
|
+
models: MODELS
|
|
274
|
+
});
|
|
275
|
+
if (decoded === void 0) throw new Error("invalid account response");
|
|
276
|
+
return decoded;
|
|
277
|
+
}
|
|
278
|
+
async function authenticatedJson(config, token, path, init) {
|
|
279
|
+
return (await authenticatedResponse(config, token, path, init)).json();
|
|
280
|
+
}
|
|
281
|
+
async function authenticatedResponse(config, token, path, init) {
|
|
282
|
+
const send = async () => {
|
|
283
|
+
const accessToken = await token.get();
|
|
284
|
+
return {
|
|
285
|
+
response: await fetch(new URL(path, config.baseURL), {
|
|
286
|
+
...init,
|
|
287
|
+
headers: {
|
|
288
|
+
...headersRecord(init.headers),
|
|
289
|
+
authorization: `Bearer ${accessToken}`
|
|
290
|
+
}
|
|
291
|
+
}),
|
|
292
|
+
accessToken
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
let result = await send();
|
|
296
|
+
if (result.response.status === 401) {
|
|
297
|
+
token.invalidate(result.accessToken);
|
|
298
|
+
result = await send();
|
|
299
|
+
}
|
|
300
|
+
const { response } = result;
|
|
301
|
+
if (!response.ok) throw await modelProxyError(response, `AWiki-hosted DeepSeek service returned HTTP ${response.status}`);
|
|
302
|
+
return response;
|
|
303
|
+
}
|
|
304
|
+
async function modelProxyError(response, fallback) {
|
|
305
|
+
let message = fallback;
|
|
306
|
+
try {
|
|
307
|
+
const body = await response.text();
|
|
308
|
+
if (body !== "") try {
|
|
309
|
+
const value = JSON.parse(body);
|
|
310
|
+
if (isRecord(value) && isRecord(value.error) && typeof value.error.message === "string") message = value.error.message;
|
|
311
|
+
} catch {
|
|
312
|
+
message = body;
|
|
313
|
+
}
|
|
314
|
+
} catch {}
|
|
315
|
+
return new LlmError(message, response.status === 401 || response.status === 403 ? "AUTH" : `HTTP_${response.status}`, { status: response.status });
|
|
316
|
+
}
|
|
317
|
+
function resolveConfig(input) {
|
|
318
|
+
const baseURL = new URL(input.baseURL ?? "https://model.awiki.info");
|
|
319
|
+
if (baseURL.username !== "" || baseURL.password !== "" || baseURL.search !== "" || baseURL.hash !== "") throw new Error("awiki-model-proxy: baseURL must not contain credentials, query, or fragment");
|
|
320
|
+
if (baseURL.protocol !== "https:" && !(baseURL.protocol === "http:" && [
|
|
321
|
+
"127.0.0.1",
|
|
322
|
+
"localhost",
|
|
323
|
+
"::1"
|
|
324
|
+
].includes(baseURL.hostname))) throw new Error("awiki-model-proxy: baseURL must use HTTPS or loopback HTTP");
|
|
325
|
+
const contextWindow = positiveInteger(input.contextWindow ?? 1e6, "contextWindow");
|
|
326
|
+
const maxTokens = positiveInteger(input.maxTokens ?? 8192, "maxTokens");
|
|
327
|
+
const skew = input.tokenRefreshSkewSeconds ?? 60;
|
|
328
|
+
if (!Number.isSafeInteger(skew) || skew < 0) throw new Error("awiki-model-proxy: tokenRefreshSkewSeconds must be a non-negative integer");
|
|
329
|
+
return {
|
|
330
|
+
baseURL,
|
|
331
|
+
contextWindow,
|
|
332
|
+
maxTokens,
|
|
333
|
+
tokenRefreshSkewMs: skew * 1e3
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function positiveInteger(value, name) {
|
|
337
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`awiki-model-proxy: ${name} must be a positive integer`);
|
|
338
|
+
return value;
|
|
339
|
+
}
|
|
340
|
+
function headersRecord(headers) {
|
|
341
|
+
return Object.fromEntries(new Headers(headers));
|
|
342
|
+
}
|
|
343
|
+
function isRecord(value) {
|
|
344
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
345
|
+
}
|
|
346
|
+
function displayMessage(error) {
|
|
347
|
+
return error instanceof Error && error.message !== "" ? error.message : "AWiki-hosted DeepSeek service is unavailable.";
|
|
348
|
+
}
|
|
349
|
+
function badRequest() {
|
|
350
|
+
return {
|
|
351
|
+
ok: false,
|
|
352
|
+
error: {
|
|
353
|
+
code: "bad-request",
|
|
354
|
+
message: "The AWiki-hosted DeepSeek request is invalid.",
|
|
355
|
+
details: { issues: [] }
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function modelUnavailable(message) {
|
|
360
|
+
return {
|
|
361
|
+
ok: false,
|
|
362
|
+
error: {
|
|
363
|
+
code: "model-unavailable",
|
|
364
|
+
message,
|
|
365
|
+
details: {
|
|
366
|
+
provider: PROVIDER,
|
|
367
|
+
model: FLASH
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function internal(message) {
|
|
373
|
+
return {
|
|
374
|
+
ok: false,
|
|
375
|
+
error: {
|
|
376
|
+
code: "internal",
|
|
377
|
+
message,
|
|
378
|
+
details: {}
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
//#endregion
|
|
383
|
+
export { Config, apply, inject, name };
|
package/lib/provider.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as RustSdkAdapter } from "./sdk-adapter-
|
|
1
|
+
import { t as RustSdkAdapter } from "./sdk-adapter-DKgkyApB.mjs";
|
|
2
2
|
import { openImCoreNodeClient } from "@awiki/im-core-node";
|
|
3
3
|
//#region lib/types/provider.js
|
|
4
4
|
/** Production AWiki provider backed by the versioned Rust IM Core Node bridge. */
|
|
@@ -14,6 +14,7 @@ function apply(ctx) {
|
|
|
14
14
|
didDomain: options.userServiceDomain,
|
|
15
15
|
userServiceEndpoint: options.userServiceUrl,
|
|
16
16
|
messageServiceEndpoint: options.messageServiceUrl,
|
|
17
|
+
mailServiceEndpoint: options.mailServiceUrl,
|
|
17
18
|
anpServiceEndpoint: options.messageServiceUrl,
|
|
18
19
|
anpServiceDid: options.messageServiceDid,
|
|
19
20
|
externalHttpAllowInsecureLoopbackForTesting: options.allowInsecureLoopbackForTesting
|