@siliconflow-official/dsh-llm-siliconflow 0.2.0-rc.1 → 0.2.0-rc.3
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 +18 -0
- package/lib/bin.js +93 -9
- package/lib/index.js +2 -2
- package/lib/types/dsh-era.d.ts +133 -0
- package/lib/types/index.d.ts +26 -0
- package/lib/types/setup.d.ts +24 -5
- package/lib/{types-BWDupDnP.js → types-IqSaZRz4.js} +156 -11
- package/package.json +48 -20
package/README.md
CHANGED
|
@@ -34,6 +34,24 @@ dsh-siliconflow-setup
|
|
|
34
34
|
export SILICONFLOW_API_KEY=sk-... # 或写入 $DSH_HOME/.credentials.yaml
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
写入 `.credentials.yaml` 采用**跨代兼容规则**(单一构建同时服务新旧两代 dsh):
|
|
38
|
+
|
|
39
|
+
- **新文件 / 扁平(无 `version`)文件 → 保持扁平写入**:旧代 dsh(credentials-local 0.1.0)原生可读;新代 dsh 会在下次启动时自动迁移为 `version: 1`。
|
|
40
|
+
- **已是 `version: 1` 的文件 → 保持 version-1 写入**:新代用户永不降级;新旧两代读路径都识别这两种布局。
|
|
41
|
+
- 其他无法识别的文档(未知键、非法结构)会明确报错而不是被改写;早期版本向导误写的顶层键会在写入时折回 `refs:`。
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
# 向导写出的新文件(扁平,两代公共分母):
|
|
45
|
+
OTHER_KEY: sk-...
|
|
46
|
+
SILICONFLOW_API_KEY: sk-...
|
|
47
|
+
|
|
48
|
+
# 新代 dsh 首次启动后自动迁移为:
|
|
49
|
+
version: 1
|
|
50
|
+
refs:
|
|
51
|
+
OTHER_KEY: sk-...
|
|
52
|
+
SILICONFLOW_API_KEY: sk-...
|
|
53
|
+
```
|
|
54
|
+
|
|
37
55
|
在包发布到 npm 之前,先用 `pnpm install && pnpm build` 构建出 `lib/`,再从本地路径安装:
|
|
38
56
|
|
|
39
57
|
```sh
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { _ as discoverChatModels, a as PUBLIC_BASE_URL, i as PROVIDER, n as DEFAULT_API_KEY_ENV, r as DEFAULT_MODELS } from "./types-IqSaZRz4.js";
|
|
3
3
|
import { createInterface } from "node:readline/promises";
|
|
4
4
|
import { stdin, stdout } from "node:process";
|
|
5
5
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
@@ -45,11 +45,66 @@ function settingsPath(home) {
|
|
|
45
45
|
function isEnoent(error) {
|
|
46
46
|
return error.code === "ENOENT";
|
|
47
47
|
}
|
|
48
|
+
/** The credentials document layout version this wizard reads and writes. */
|
|
49
|
+
const CREDENTIALS_LAYOUT_VERSION = 1;
|
|
50
|
+
/** POSIX identifier rule the credentials seam addresses references by. */
|
|
51
|
+
const REF_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
48
52
|
/**
|
|
49
|
-
*
|
|
53
|
+
* Recognize one parsed credentials document.
|
|
54
|
+
* @param document - the parsed document; parse errors make it unrecognizable.
|
|
55
|
+
* @returns the admitted layout with its references — plus, under `extraKeys`,
|
|
56
|
+
* any top-level key a pre-fix release of this wizard left beside `version`,
|
|
57
|
+
* held out of `entries` so reads treat them as absent while the write path
|
|
58
|
+
* folds them back under `refs` — or `undefined` when the document does not
|
|
59
|
+
* parse, holds an unknown `version`, carries a `refs` section that is not a
|
|
60
|
+
* mapping of reference names to non-empty strings, or holds any other
|
|
61
|
+
* top-level entry this build cannot attribute to its own predecessor.
|
|
62
|
+
*/
|
|
63
|
+
function admitCredentialsDocument(document) {
|
|
64
|
+
if (document.errors.length > 0) return void 0;
|
|
65
|
+
const root = document.toJS() ?? {};
|
|
66
|
+
if (typeof root !== "object" || root === null) return void 0;
|
|
67
|
+
const fields = root;
|
|
68
|
+
const entries = /* @__PURE__ */ new Map();
|
|
69
|
+
if (!("version" in fields)) {
|
|
70
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
71
|
+
if (!REF_NAME_PATTERN.test(key)) return void 0;
|
|
72
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
73
|
+
entries.set(key, value);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
kind: "flat",
|
|
77
|
+
entries
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (fields.version !== CREDENTIALS_LAYOUT_VERSION) return void 0;
|
|
81
|
+
const refs = fields["refs"];
|
|
82
|
+
if (refs !== void 0) {
|
|
83
|
+
if (typeof refs !== "object" || refs === null) return void 0;
|
|
84
|
+
for (const [key, value] of Object.entries(refs)) {
|
|
85
|
+
if (!REF_NAME_PATTERN.test(key)) return void 0;
|
|
86
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
87
|
+
entries.set(key, value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const extraKeys = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
92
|
+
if (key === "version" || key === "refs" || key === "records") continue;
|
|
93
|
+
if (!REF_NAME_PATTERN.test(key) || typeof value !== "string" || value.length === 0) return void 0;
|
|
94
|
+
extraKeys.set(key, value);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
kind: "versioned",
|
|
98
|
+
entries,
|
|
99
|
+
...extraKeys.size > 0 ? { extraKeys } : {}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Read one credential reference from a versioned or pre-release flat document.
|
|
50
104
|
* @param path - the credentials document path.
|
|
51
|
-
* @param keyEnv - the
|
|
52
|
-
* @returns the stored value, or `undefined` when
|
|
105
|
+
* @param keyEnv - the reference name to read (e.g. `SILICONFLOW_API_KEY`).
|
|
106
|
+
* @returns the stored value, or `undefined` when the reference is absent, the
|
|
107
|
+
* file is missing, or the document is not a recognizable layout.
|
|
53
108
|
*/
|
|
54
109
|
async function readCredential(path, keyEnv) {
|
|
55
110
|
let text;
|
|
@@ -59,18 +114,47 @@ async function readCredential(path, keyEnv) {
|
|
|
59
114
|
if (isEnoent(error)) return void 0;
|
|
60
115
|
throw error;
|
|
61
116
|
}
|
|
62
|
-
const
|
|
63
|
-
|
|
117
|
+
const admitted = admitCredentialsDocument(parseDocument(text));
|
|
118
|
+
if (admitted === void 0) return void 0;
|
|
119
|
+
return admitted.entries.get(keyEnv);
|
|
64
120
|
}
|
|
65
121
|
/**
|
|
66
|
-
*
|
|
122
|
+
* Write one credential reference, preserving the document's own layout family
|
|
123
|
+
* and every other entry and comment.
|
|
124
|
+
*
|
|
125
|
+
* Cross-era rule (0.2.0-rc.3): which layout a FRESH document gets is decided
|
|
126
|
+
* by what both dsh eras can read, not by this build's version clock. The OLD
|
|
127
|
+
* credentials-local (0.1.0-rc.8) reads only the flat layout; the NEW one
|
|
128
|
+
* (0.1.1+) reads only version-1 but migrates a version-less flat document on
|
|
129
|
+
* its next boot. Flat is therefore the common denominator: a fresh or flat
|
|
130
|
+
* document stays flat under this write, and the running dsh upgrades it when
|
|
131
|
+
* that dsh is new-era. An existing versioned document keeps version-1 so a
|
|
132
|
+
* new-era user is never downgraded; an old-era user with a versioned document
|
|
133
|
+
* still has a file old dsh rejects — that file predates this rule and is
|
|
134
|
+
* repaired by re-running setup on a flat base or by hand.
|
|
135
|
+
*
|
|
136
|
+
* A top-level key a pre-fix release of this wizard left beside `version` is
|
|
137
|
+
* folded back under `refs`, so one run repairs the file its predecessor
|
|
138
|
+
* corrupted. An unrecognized document fails loud instead of being rewritten —
|
|
139
|
+
* a silent rewrite would hide why the running harness rejects it.
|
|
67
140
|
* @param path - the credentials document path; created when absent.
|
|
68
|
-
* @param keyEnv - the
|
|
141
|
+
* @param keyEnv - the reference name to write.
|
|
69
142
|
* @param key - the value.
|
|
143
|
+
* @throws when the document exists but is not a recognizable credentials layout.
|
|
70
144
|
*/
|
|
71
145
|
async function writeCredential(path, keyEnv, key) {
|
|
72
146
|
const doc = await loadDocument(path);
|
|
73
|
-
doc
|
|
147
|
+
const admitted = admitCredentialsDocument(doc);
|
|
148
|
+
if (admitted === void 0) throw new Error(`setup: ${path} is not a recognizable credentials document (expected version 1 with a refs section, or the pre-release flat layout); fix it before running setup`);
|
|
149
|
+
if (admitted.kind === "flat") {
|
|
150
|
+
doc.setIn([keyEnv], key);
|
|
151
|
+
await persistDocument(path, doc);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
for (const extra of admitted.extraKeys?.keys() ?? []) doc.deleteIn([extra]);
|
|
155
|
+
for (const [extra, value] of admitted.extraKeys ?? /* @__PURE__ */ new Map()) doc.setIn(["refs", extra], value);
|
|
156
|
+
doc.setIn(["version"], CREDENTIALS_LAYOUT_VERSION);
|
|
157
|
+
doc.setIn(["refs", keyEnv], key);
|
|
74
158
|
await persistDocument(path, doc);
|
|
75
159
|
}
|
|
76
160
|
/**
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
export { Config, DEFAULT_API_KEY_ENV, DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_TOKENS, DEFAULT_MODELS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DISCOVERY_TTL_MS, PROVIDER, PUBLIC_BASE_URL, SiliconFlowAdapter, apply, discoverChatModels, inferInputModalities, inject, listingUrl, name, readListing, resolveAdapterOptions };
|
|
1
|
+
import { _ as discoverChatModels, a as PUBLIC_BASE_URL, c as inject, d as DEFAULT_CONTEXT_WINDOW, f as DEFAULT_MAX_TOKENS, g as inferInputModalities, h as SiliconFlowAdapter, i as PROVIDER, l as name, m as DISCOVERY_TTL_MS, n as DEFAULT_API_KEY_ENV, o as apply, p as DEFAULT_STREAM_IDLE_TIMEOUT_MS, r as DEFAULT_MODELS, s as assembleSettingsSection, t as Config, u as resolveAdapterOptions, v as listingUrl, y as readListing } from "./types-IqSaZRz4.js";
|
|
2
|
+
export { Config, DEFAULT_API_KEY_ENV, DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_TOKENS, DEFAULT_MODELS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DISCOVERY_TTL_MS, PROVIDER, PUBLIC_BASE_URL, SiliconFlowAdapter, apply, assembleSettingsSection, discoverChatModels, inferInputModalities, inject, listingUrl, name, readListing, resolveAdapterOptions };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-build cross-era compatibility for the DeepSeek Harness dsh family.
|
|
3
|
+
*
|
|
4
|
+
* One published plugin build must run inside TWO dsh runtime closures:
|
|
5
|
+
*
|
|
6
|
+
* - the NEW era (dsh 0.1.2+ LLM family, first shipped 2026-08-30):
|
|
7
|
+
* `ToolCallId` brand, `version: 1` credentials layout (`refs:` nesting,
|
|
8
|
+
* unknown top-level keys hard-fail), settings installed via the service
|
|
9
|
+
* method `installSection`, model discovery via `LlmModelDiscoveryOperation`
|
|
10
|
+
* (extends `LlmModelDiscoveryRequest` with optional `signal`).
|
|
11
|
+
* - the OLD era (dsh-llm 0.1.1 and earlier; credentials-local 0.1.0):
|
|
12
|
+
* `CallId` brand, flat top-level credentials (no `version` key),
|
|
13
|
+
* settings installed via the module function `installSettingsSection`,
|
|
14
|
+
* model discovery via `LlmModelDiscoveryRequest` carrying `signal`.
|
|
15
|
+
*
|
|
16
|
+
* Compatibility rules this module relies on, established against real
|
|
17
|
+
* published packages (npm tarballs), not assumptions:
|
|
18
|
+
*
|
|
19
|
+
* 1. ESM link-time export validation only applies to NAMED imports
|
|
20
|
+
* (`import { ToolCallId }`). A missing named export throws SyntaxError at
|
|
21
|
+
* link time and the module never evaluates — exactly the reported
|
|
22
|
+
* `SyntaxError: … does not provide an export named 'CallId'`. A NAMESPACE
|
|
23
|
+
* import (`import * as llm`) links unconditionally; missing members are
|
|
24
|
+
* simply `undefined` at runtime. Every era-sensitive value therefore comes
|
|
25
|
+
* from the namespace object, never from a named import.
|
|
26
|
+
* 2. Exports BOTH eras ship (verified in 0.1.1-rc.2 and 0.1.5-rc.2):
|
|
27
|
+
* EMPTY_RESPONSE_CODE, LlmError, LlmAdapter, ProviderRequestId,
|
|
28
|
+
* QUOTA_EXCEEDED_CODE, RetryPolicySchema, assertUsableApiKey,
|
|
29
|
+
* resolveRetryPolicy, attributionHeaders, isQuotaExceededError,
|
|
30
|
+
* isContextWindowExceededError, CONTEXT_WINDOW_EXCEEDED_CODE. Named
|
|
31
|
+
* imports of these stay safe in both eras.
|
|
32
|
+
* 3. `dsh-util-values` postdates the OLD era entirely (404 there); its one
|
|
33
|
+
* consumer in this plugin, `deepEqualJson`, is inlined below.
|
|
34
|
+
*
|
|
35
|
+
* The era probe: `typeof ns.ToolCallId === 'function'`. NEW era exports
|
|
36
|
+
* ToolCallId; OLD era exports CallId instead. One probe classifies the
|
|
37
|
+
* closure and stays stable under future renames.
|
|
38
|
+
*
|
|
39
|
+
* @module dsh-llm-siliconflow/dsh-era
|
|
40
|
+
*/
|
|
41
|
+
import type { ToolCallId as ToolCallIdType } from '@deepseek-ai/dsh-llm';
|
|
42
|
+
/**
|
|
43
|
+
* The raw namespace, exported for era-sensitive call sites. Typed loosely:
|
|
44
|
+
* the OLD-era variant of a member is unknown to the NEW-era .d.ts and vice
|
|
45
|
+
* versa, so era-shape access happens through this record, never the typed
|
|
46
|
+
* import.
|
|
47
|
+
*/
|
|
48
|
+
export declare const dshLLM: Record<string, unknown>;
|
|
49
|
+
/** True when the running dsh closure is the NEW era (ToolCallId present). */
|
|
50
|
+
export declare const isNewEra: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Construct a provider tool-call id brand for the running era: `ToolCallId`
|
|
53
|
+
* in the NEW era, `CallId` in the OLD. The constructor is resolved at call
|
|
54
|
+
* time from the live namespace; a closure exposing neither fails loud.
|
|
55
|
+
*
|
|
56
|
+
* @param raw - the raw provider id, e.g. `block.callId ?? ''`.
|
|
57
|
+
* @returns the era-correct branded id.
|
|
58
|
+
*/
|
|
59
|
+
export declare function toolCallIdOf(raw: string): ToolCallIdType;
|
|
60
|
+
/**
|
|
61
|
+
* Structural JSON equality over plain values (the shape settings documents
|
|
62
|
+
* and retry policies are made of). Inlined from dsh-util-values so the plugin
|
|
63
|
+
* carries no dependency on a NEW-era-only package: the OLD-era closure cannot
|
|
64
|
+
* even install dsh-util-values, which used to break npm resolution of this
|
|
65
|
+
* plugin under old dsh homes.
|
|
66
|
+
*
|
|
67
|
+
* Owned here over the value domain the plugin actually observes (plain JSON
|
|
68
|
+
* documents): recursion by key set, so object member order never matters —
|
|
69
|
+
* the upstream package compares structurally, and JSON.stringify would make
|
|
70
|
+
* order significant, corrupting that contract.
|
|
71
|
+
*
|
|
72
|
+
* @param a - one plain JSON value.
|
|
73
|
+
* @param b - another plain JSON value.
|
|
74
|
+
* @returns true when both represent the same JSON value.
|
|
75
|
+
*/
|
|
76
|
+
export declare function deepEqualJson(a: unknown, b: unknown): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* The era-neutral discovery-request shape one callback can serve both eras
|
|
79
|
+
* from: the fields the SiliconFlow discovery callback reads. In the OLD era
|
|
80
|
+
* the caller passes `LlmModelDiscoveryRequest` (signal present as a field);
|
|
81
|
+
* in the NEW era `LlmModelDiscoveryOperation` (same field, optional).
|
|
82
|
+
*/
|
|
83
|
+
export interface ModelDiscoveryShape {
|
|
84
|
+
/** Route being edited, when it edits an existing one. */
|
|
85
|
+
provider?: string;
|
|
86
|
+
/** Endpoint to interrogate. */
|
|
87
|
+
baseURL?: string;
|
|
88
|
+
/** Wire protocol the endpoint speaks, when the draft names one. */
|
|
89
|
+
api?: string;
|
|
90
|
+
/** Credential for this interrogation alone; never stored by the harness. */
|
|
91
|
+
apiKey?: string;
|
|
92
|
+
/** Caller cancellation; present in both eras, optional in the new one. */
|
|
93
|
+
signal?: AbortSignal;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The era-neutral settings-hooks shape. The OLD and NEW installers take
|
|
97
|
+
* identical hook objects; only the mount point moved between eras.
|
|
98
|
+
* Parameterized on the entry type so it flows through without variance
|
|
99
|
+
* friction against exactOptionalPropertyTypes.
|
|
100
|
+
*/
|
|
101
|
+
export interface EraSettingsHooks<T> {
|
|
102
|
+
setSource: (source: () => T) => void;
|
|
103
|
+
onChange: () => void;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Structural mirror of the NEW-era settings namespace input type (a lowercase
|
|
107
|
+
* hyphenated identifier). Declared locally so this plugin typechecks under the
|
|
108
|
+
* OLD-era closure, where `@deepseek-ai/dsh-settings` exports a differently
|
|
109
|
+
* shaped `SettingsNamespace`; the namespace value itself is identical either way.
|
|
110
|
+
*/
|
|
111
|
+
export type SettingsNamespaceInput = string;
|
|
112
|
+
/**
|
|
113
|
+
* The settings package namespace as a plain record. The OLD era exports
|
|
114
|
+
* installSettingsSection as a MODULE-LEVEL function (it performs its own
|
|
115
|
+
* ctx.inject(['settings']) internally); the NEW era dropped that export and
|
|
116
|
+
* moved the installer onto the service class as installSection. Era probes
|
|
117
|
+
* must consult BOTH surfaces: the module namespace for the old path, the
|
|
118
|
+
* service object for the new one — probing only one proved wrong against
|
|
119
|
+
* real packages (verified against 0.1.1-rc.2 and 0.1.5-rc.2 tarballs).
|
|
120
|
+
*/
|
|
121
|
+
export declare const dshSettings: Record<string, unknown>;
|
|
122
|
+
/**
|
|
123
|
+
* The era-union settings service seen by the plugin: the NEW era exposes
|
|
124
|
+
* installSection as a service method; the OLD era exported
|
|
125
|
+
* installSettingsSection as a module function (also re-exported by the
|
|
126
|
+
* service object). Signatures are identical (owner, ns, schema, entry, hooks)
|
|
127
|
+
* — only the mount point moved.
|
|
128
|
+
*/
|
|
129
|
+
export interface EraSettingsService {
|
|
130
|
+
installSection?: <T>(owner: unknown, ns: SettingsNamespaceInput, schema: unknown, entry: T, hooks: EraSettingsHooks<T>) => void;
|
|
131
|
+
installSettingsSection?: <T>(owner: unknown, ns: SettingsNamespaceInput, schema: unknown, entry: T, hooks: EraSettingsHooks<T>) => void;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=dsh-era.d.ts.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
18
18
|
import z from '@deepseek-ai/schemastery';
|
|
19
19
|
import type { RetryPolicyConfig } from '@deepseek-ai/dsh-llm';
|
|
20
20
|
import { type LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment';
|
|
21
|
+
import { type EraSettingsHooks, type EraSettingsService } from './dsh-era.ts';
|
|
21
22
|
import type { SiliconFlowCatalogModel, SiliconFlowConnectionOptions } from './adapter.ts';
|
|
22
23
|
export { DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_TOKENS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DISCOVERY_TTL_MS, SiliconFlowAdapter, } from './adapter.ts';
|
|
23
24
|
export { inferInputModalities } from './adapter.ts';
|
|
@@ -91,4 +92,29 @@ export type ResolvedSiliconFlowOptions = SiliconFlowConnectionOptions;
|
|
|
91
92
|
*/
|
|
92
93
|
export declare function resolveAdapterOptions(config: Config, environment?: LaunchEnvironmentSnapshot): ResolvedSiliconFlowOptions;
|
|
93
94
|
export declare function apply(ctx: Context, config: Config): void;
|
|
95
|
+
/**
|
|
96
|
+
* Install the settings through whichever surface the running era exposes.
|
|
97
|
+
*
|
|
98
|
+
* NEW era (dsh-settings 0.1.2+): the injected settings SERVICE carries the
|
|
99
|
+
* installer as the method `installSection` — invoked as a method so the
|
|
100
|
+
* service's registration state stays bound.
|
|
101
|
+
*
|
|
102
|
+
* OLD era (dsh-settings 0.1.1): the installer is the MODULE-LEVEL function
|
|
103
|
+
* `installSettingsSection` from '@deepseek-ai/dsh-settings' — it performs
|
|
104
|
+
* its own ctx.inject(['settings']) internally, so it is called directly with
|
|
105
|
+
* (owner, ns, schema, entry, hooks). No service argument is involved.
|
|
106
|
+
*
|
|
107
|
+
* Probing taught against real packages (0.1.1-rc.2 / 0.1.5-rc.2 tarballs):
|
|
108
|
+
* the OLD service object has NO installSettingsSection member, and the NEW
|
|
109
|
+
* module has NO installSettingsSection export — probing a single surface
|
|
110
|
+
* would throw on the other era. Both surfaces are checked in order; when
|
|
111
|
+
* the running dsh exposes neither, this fails loud instead of silently
|
|
112
|
+
* dropping the section.
|
|
113
|
+
*/
|
|
114
|
+
export declare function assembleSettingsSection<T>(service: EraSettingsService, args: {
|
|
115
|
+
owner: Context;
|
|
116
|
+
ns: string;
|
|
117
|
+
schema: unknown;
|
|
118
|
+
entry: T;
|
|
119
|
+
} & EraSettingsHooks<T>): void;
|
|
94
120
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/setup.d.ts
CHANGED
|
@@ -56,17 +56,36 @@ export declare function credentialsPath(home: string): string;
|
|
|
56
56
|
/** The settings document path under a harness home. */
|
|
57
57
|
export declare function settingsPath(home: string): string;
|
|
58
58
|
/**
|
|
59
|
-
* Read one credential reference from a
|
|
59
|
+
* Read one credential reference from a versioned or pre-release flat document.
|
|
60
60
|
* @param path - the credentials document path.
|
|
61
|
-
* @param keyEnv - the
|
|
62
|
-
* @returns the stored value, or `undefined` when
|
|
61
|
+
* @param keyEnv - the reference name to read (e.g. `SILICONFLOW_API_KEY`).
|
|
62
|
+
* @returns the stored value, or `undefined` when the reference is absent, the
|
|
63
|
+
* file is missing, or the document is not a recognizable layout.
|
|
63
64
|
*/
|
|
64
65
|
export declare function readCredential(path: string, keyEnv: string): Promise<string | undefined>;
|
|
65
66
|
/**
|
|
66
|
-
*
|
|
67
|
+
* Write one credential reference, preserving the document's own layout family
|
|
68
|
+
* and every other entry and comment.
|
|
69
|
+
*
|
|
70
|
+
* Cross-era rule (0.2.0-rc.3): which layout a FRESH document gets is decided
|
|
71
|
+
* by what both dsh eras can read, not by this build's version clock. The OLD
|
|
72
|
+
* credentials-local (0.1.0-rc.8) reads only the flat layout; the NEW one
|
|
73
|
+
* (0.1.1+) reads only version-1 but migrates a version-less flat document on
|
|
74
|
+
* its next boot. Flat is therefore the common denominator: a fresh or flat
|
|
75
|
+
* document stays flat under this write, and the running dsh upgrades it when
|
|
76
|
+
* that dsh is new-era. An existing versioned document keeps version-1 so a
|
|
77
|
+
* new-era user is never downgraded; an old-era user with a versioned document
|
|
78
|
+
* still has a file old dsh rejects — that file predates this rule and is
|
|
79
|
+
* repaired by re-running setup on a flat base or by hand.
|
|
80
|
+
*
|
|
81
|
+
* A top-level key a pre-fix release of this wizard left beside `version` is
|
|
82
|
+
* folded back under `refs`, so one run repairs the file its predecessor
|
|
83
|
+
* corrupted. An unrecognized document fails loud instead of being rewritten —
|
|
84
|
+
* a silent rewrite would hide why the running harness rejects it.
|
|
67
85
|
* @param path - the credentials document path; created when absent.
|
|
68
|
-
* @param keyEnv - the
|
|
86
|
+
* @param keyEnv - the reference name to write.
|
|
69
87
|
* @param key - the value.
|
|
88
|
+
* @throws when the document exists but is not a recognizable credentials layout.
|
|
70
89
|
*/
|
|
71
90
|
export declare function writeCredential(path: string, keyEnv: string, key: string): Promise<void>;
|
|
72
91
|
/**
|
|
@@ -1,11 +1,119 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
-
import
|
|
2
|
+
import * as llm from "@deepseek-ai/dsh-llm";
|
|
3
|
+
import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, LlmAdapter, LlmError, ProviderRequestId, QUOTA_EXCEEDED_CODE, RetryPolicySchema, assertUsableApiKey, attributionHeaders, isContextWindowExceededError, isQuotaExceededError, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
3
4
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
4
5
|
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
5
|
-
import
|
|
6
|
+
import * as settingsModule from "@deepseek-ai/dsh-settings";
|
|
6
7
|
import { MAX_TIMER_DELAY_MS, idleWatchdog, timeoutOf } from "@deepseek-ai/dsh-timeout";
|
|
7
8
|
import { getOrCreateAnonymousUserId } from "@deepseek-ai/dsh-anonymous-user-id";
|
|
8
9
|
import { EventSourceParserStream } from "eventsource-parser/stream";
|
|
10
|
+
//#region lib/types/dsh-era.js
|
|
11
|
+
/**
|
|
12
|
+
* Single-build cross-era compatibility for the DeepSeek Harness dsh family.
|
|
13
|
+
*
|
|
14
|
+
* One published plugin build must run inside TWO dsh runtime closures:
|
|
15
|
+
*
|
|
16
|
+
* - the NEW era (dsh 0.1.2+ LLM family, first shipped 2026-08-30):
|
|
17
|
+
* `ToolCallId` brand, `version: 1` credentials layout (`refs:` nesting,
|
|
18
|
+
* unknown top-level keys hard-fail), settings installed via the service
|
|
19
|
+
* method `installSection`, model discovery via `LlmModelDiscoveryOperation`
|
|
20
|
+
* (extends `LlmModelDiscoveryRequest` with optional `signal`).
|
|
21
|
+
* - the OLD era (dsh-llm 0.1.1 and earlier; credentials-local 0.1.0):
|
|
22
|
+
* `CallId` brand, flat top-level credentials (no `version` key),
|
|
23
|
+
* settings installed via the module function `installSettingsSection`,
|
|
24
|
+
* model discovery via `LlmModelDiscoveryRequest` carrying `signal`.
|
|
25
|
+
*
|
|
26
|
+
* Compatibility rules this module relies on, established against real
|
|
27
|
+
* published packages (npm tarballs), not assumptions:
|
|
28
|
+
*
|
|
29
|
+
* 1. ESM link-time export validation only applies to NAMED imports
|
|
30
|
+
* (`import { ToolCallId }`). A missing named export throws SyntaxError at
|
|
31
|
+
* link time and the module never evaluates — exactly the reported
|
|
32
|
+
* `SyntaxError: … does not provide an export named 'CallId'`. A NAMESPACE
|
|
33
|
+
* import (`import * as llm`) links unconditionally; missing members are
|
|
34
|
+
* simply `undefined` at runtime. Every era-sensitive value therefore comes
|
|
35
|
+
* from the namespace object, never from a named import.
|
|
36
|
+
* 2. Exports BOTH eras ship (verified in 0.1.1-rc.2 and 0.1.5-rc.2):
|
|
37
|
+
* EMPTY_RESPONSE_CODE, LlmError, LlmAdapter, ProviderRequestId,
|
|
38
|
+
* QUOTA_EXCEEDED_CODE, RetryPolicySchema, assertUsableApiKey,
|
|
39
|
+
* resolveRetryPolicy, attributionHeaders, isQuotaExceededError,
|
|
40
|
+
* isContextWindowExceededError, CONTEXT_WINDOW_EXCEEDED_CODE. Named
|
|
41
|
+
* imports of these stay safe in both eras.
|
|
42
|
+
* 3. `dsh-util-values` postdates the OLD era entirely (404 there); its one
|
|
43
|
+
* consumer in this plugin, `deepEqualJson`, is inlined below.
|
|
44
|
+
*
|
|
45
|
+
* The era probe: `typeof ns.ToolCallId === 'function'`. NEW era exports
|
|
46
|
+
* ToolCallId; OLD era exports CallId instead. One probe classifies the
|
|
47
|
+
* closure and stays stable under future renames.
|
|
48
|
+
*
|
|
49
|
+
* @module dsh-llm-siliconflow/dsh-era
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* The raw namespace, exported for era-sensitive call sites. Typed loosely:
|
|
53
|
+
* the OLD-era variant of a member is unknown to the NEW-era .d.ts and vice
|
|
54
|
+
* versa, so era-shape access happens through this record, never the typed
|
|
55
|
+
* import.
|
|
56
|
+
*/
|
|
57
|
+
const dshLLM = llm;
|
|
58
|
+
dshLLM["ToolCallId"];
|
|
59
|
+
/**
|
|
60
|
+
* Construct a provider tool-call id brand for the running era: `ToolCallId`
|
|
61
|
+
* in the NEW era, `CallId` in the OLD. The constructor is resolved at call
|
|
62
|
+
* time from the live namespace; a closure exposing neither fails loud.
|
|
63
|
+
*
|
|
64
|
+
* @param raw - the raw provider id, e.g. `block.callId ?? ''`.
|
|
65
|
+
* @returns the era-correct branded id.
|
|
66
|
+
*/
|
|
67
|
+
function toolCallIdOf(raw) {
|
|
68
|
+
const ctor = dshLLM["ToolCallId"] ?? dshLLM["CallId"];
|
|
69
|
+
if (ctor === void 0) throw new Error("neither ToolCallId nor CallId is exported by @deepseek-ai/dsh-llm");
|
|
70
|
+
return ctor(raw);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Structural JSON equality over plain values (the shape settings documents
|
|
74
|
+
* and retry policies are made of). Inlined from dsh-util-values so the plugin
|
|
75
|
+
* carries no dependency on a NEW-era-only package: the OLD-era closure cannot
|
|
76
|
+
* even install dsh-util-values, which used to break npm resolution of this
|
|
77
|
+
* plugin under old dsh homes.
|
|
78
|
+
*
|
|
79
|
+
* Owned here over the value domain the plugin actually observes (plain JSON
|
|
80
|
+
* documents): recursion by key set, so object member order never matters —
|
|
81
|
+
* the upstream package compares structurally, and JSON.stringify would make
|
|
82
|
+
* order significant, corrupting that contract.
|
|
83
|
+
*
|
|
84
|
+
* @param a - one plain JSON value.
|
|
85
|
+
* @param b - another plain JSON value.
|
|
86
|
+
* @returns true when both represent the same JSON value.
|
|
87
|
+
*/
|
|
88
|
+
function deepEqualJson(a, b) {
|
|
89
|
+
if (a === b) return true;
|
|
90
|
+
if (a === null || b === null) return false;
|
|
91
|
+
if (typeof a !== typeof b) return false;
|
|
92
|
+
if (typeof a !== "object") return a === b;
|
|
93
|
+
const ao = a;
|
|
94
|
+
const bo = b;
|
|
95
|
+
if (Array.isArray(ao) !== Array.isArray(bo)) return false;
|
|
96
|
+
if (Array.isArray(ao) && Array.isArray(bo)) {
|
|
97
|
+
if (ao.length !== bo.length) return false;
|
|
98
|
+
return ao.every((item, i) => deepEqualJson(item, bo[i]));
|
|
99
|
+
}
|
|
100
|
+
const ak = Object.keys(ao);
|
|
101
|
+
const bk = Object.keys(bo);
|
|
102
|
+
if (ak.length !== bk.length) return false;
|
|
103
|
+
if (!ak.every((k) => k in bo)) return false;
|
|
104
|
+
return ak.every((k) => deepEqualJson(ao[k], bo[k]));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The settings package namespace as a plain record. The OLD era exports
|
|
108
|
+
* installSettingsSection as a MODULE-LEVEL function (it performs its own
|
|
109
|
+
* ctx.inject(['settings']) internally); the NEW era dropped that export and
|
|
110
|
+
* moved the installer onto the service class as installSection. Era probes
|
|
111
|
+
* must consult BOTH surfaces: the module namespace for the old path, the
|
|
112
|
+
* service object for the new one — probing only one proved wrong against
|
|
113
|
+
* real packages (verified against 0.1.1-rc.2 and 0.1.5-rc.2 tarballs).
|
|
114
|
+
*/
|
|
115
|
+
const dshSettings = settingsModule;
|
|
116
|
+
//#endregion
|
|
9
117
|
//#region lib/types/discovery.js
|
|
10
118
|
/**
|
|
11
119
|
* Interrogate the SiliconFlow (OpenAI-compatible) `GET /models` listing for
|
|
@@ -408,7 +516,7 @@ function closeBlock(block) {
|
|
|
408
516
|
};
|
|
409
517
|
case "tool-call": return {
|
|
410
518
|
type: "tool-call",
|
|
411
|
-
id:
|
|
519
|
+
id: toolCallIdOf(block.callId ?? ""),
|
|
412
520
|
name: block.name ?? "",
|
|
413
521
|
arguments: block.text
|
|
414
522
|
};
|
|
@@ -523,7 +631,7 @@ async function* translate(payloads) {
|
|
|
523
631
|
yield {
|
|
524
632
|
type: "tool-call-delta",
|
|
525
633
|
index: block.index,
|
|
526
|
-
id:
|
|
634
|
+
id: toolCallIdOf(block.callId ?? ""),
|
|
527
635
|
...block.name !== void 0 ? { name: block.name } : {},
|
|
528
636
|
argumentsDelta: fragment
|
|
529
637
|
};
|
|
@@ -896,7 +1004,7 @@ var SiliconFlowAdapter = class extends LlmAdapter {
|
|
|
896
1004
|
*/
|
|
897
1005
|
const name = "llm-siliconflow";
|
|
898
1006
|
const inject = ["llm"];
|
|
899
|
-
const NS =
|
|
1007
|
+
const NS = "llm-siliconflow";
|
|
900
1008
|
/** Credential reference this plugin reads by default, also used by the setup CLI. */
|
|
901
1009
|
const DEFAULT_API_KEY_ENV = "SILICONFLOW_API_KEY";
|
|
902
1010
|
/** The single provider route this plugin owns. */
|
|
@@ -1125,12 +1233,49 @@ function apply(ctx, config) {
|
|
|
1125
1233
|
registration.replace([PROVIDER]);
|
|
1126
1234
|
registeredPolicy = policy;
|
|
1127
1235
|
};
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1236
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
1237
|
+
assembleSettingsSection(settingsCtx.settings, {
|
|
1238
|
+
owner: ctx,
|
|
1239
|
+
ns: NS,
|
|
1240
|
+
schema: Config,
|
|
1241
|
+
entry: config,
|
|
1242
|
+
setSource: (source) => {
|
|
1243
|
+
current = source;
|
|
1244
|
+
},
|
|
1245
|
+
onChange: ensureRegistrationFacts
|
|
1246
|
+
});
|
|
1133
1247
|
});
|
|
1134
1248
|
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Install the settings through whichever surface the running era exposes.
|
|
1251
|
+
*
|
|
1252
|
+
* NEW era (dsh-settings 0.1.2+): the injected settings SERVICE carries the
|
|
1253
|
+
* installer as the method `installSection` — invoked as a method so the
|
|
1254
|
+
* service's registration state stays bound.
|
|
1255
|
+
*
|
|
1256
|
+
* OLD era (dsh-settings 0.1.1): the installer is the MODULE-LEVEL function
|
|
1257
|
+
* `installSettingsSection` from '@deepseek-ai/dsh-settings' — it performs
|
|
1258
|
+
* its own ctx.inject(['settings']) internally, so it is called directly with
|
|
1259
|
+
* (owner, ns, schema, entry, hooks). No service argument is involved.
|
|
1260
|
+
*
|
|
1261
|
+
* Probing taught against real packages (0.1.1-rc.2 / 0.1.5-rc.2 tarballs):
|
|
1262
|
+
* the OLD service object has NO installSettingsSection member, and the NEW
|
|
1263
|
+
* module has NO installSettingsSection export — probing a single surface
|
|
1264
|
+
* would throw on the other era. Both surfaces are checked in order; when
|
|
1265
|
+
* the running dsh exposes neither, this fails loud instead of silently
|
|
1266
|
+
* dropping the section.
|
|
1267
|
+
*/
|
|
1268
|
+
function assembleSettingsSection(service, args) {
|
|
1269
|
+
if (typeof service.installSection === "function") {
|
|
1270
|
+
service.installSection(args.owner, args.ns, args.schema, args.entry, args);
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
const oldInstall = dshSettings["installSettingsSection"];
|
|
1274
|
+
if (typeof oldInstall === "function") {
|
|
1275
|
+
oldInstall(args.owner, args.ns, args.schema, args.entry, args);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
throw new Error("settings install: neither service.installSection (new era) nor module installSettingsSection (old era) is available");
|
|
1279
|
+
}
|
|
1135
1280
|
//#endregion
|
|
1136
|
-
export {
|
|
1281
|
+
export { discoverChatModels as _, PUBLIC_BASE_URL as a, inject as c, DEFAULT_CONTEXT_WINDOW as d, DEFAULT_MAX_TOKENS as f, inferInputModalities as g, SiliconFlowAdapter as h, PROVIDER as i, name as l, DISCOVERY_TTL_MS as m, DEFAULT_API_KEY_ENV as n, apply as o, DEFAULT_STREAM_IDLE_TIMEOUT_MS as p, DEFAULT_MODELS as r, assembleSettingsSection as s, Config as t, resolveAdapterOptions as u, listingUrl as v, readListing as y };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siliconflow-official/dsh-llm-siliconflow",
|
|
3
3
|
"description": "SiliconFlow (OpenAI-compatible) chat-completions adapter plugin for the DeepSeek Harness LLM seam",
|
|
4
|
-
"version": "0.2.0-rc.
|
|
4
|
+
"version": "0.2.0-rc.3",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
47
|
-
"@deepseek-ai/dsh-anonymous-user-id": "
|
|
48
|
-
"@deepseek-ai/dsh-attachment": "
|
|
49
|
-
"@deepseek-ai/dsh-credentials": "
|
|
50
|
-
"@deepseek-ai/dsh-invariants": "
|
|
51
|
-
"@deepseek-ai/dsh-launch-environment": "
|
|
52
|
-
"@deepseek-ai/dsh-llm": "
|
|
53
|
-
"@deepseek-ai/dsh-settings": "
|
|
54
|
-
"@deepseek-ai/dsh-timeout": "
|
|
47
|
+
"@deepseek-ai/dsh-anonymous-user-id": "*",
|
|
48
|
+
"@deepseek-ai/dsh-attachment": "*",
|
|
49
|
+
"@deepseek-ai/dsh-credentials": "*",
|
|
50
|
+
"@deepseek-ai/dsh-invariants": "*",
|
|
51
|
+
"@deepseek-ai/dsh-launch-environment": "*",
|
|
52
|
+
"@deepseek-ai/dsh-llm": "*",
|
|
53
|
+
"@deepseek-ai/dsh-settings": "*",
|
|
54
|
+
"@deepseek-ai/dsh-timeout": "*"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
@@ -59,18 +59,46 @@
|
|
|
59
59
|
"yaml": "^2.9.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
63
|
-
"@deepseek-ai/dsh-anonymous-user-id": "
|
|
64
|
-
"@deepseek-ai/dsh-attachment": "
|
|
65
|
-
"@deepseek-ai/dsh-credentials": "
|
|
66
|
-
"@deepseek-ai/dsh-invariants": "
|
|
67
|
-
"@deepseek-ai/dsh-launch-environment": "
|
|
68
|
-
"@deepseek-ai/dsh-llm": "
|
|
69
|
-
"@deepseek-ai/dsh-settings": "
|
|
70
|
-
"@deepseek-ai/dsh-timeout": "
|
|
62
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
63
|
+
"@deepseek-ai/dsh-anonymous-user-id": "0.1.5-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-attachment": "0.1.5-rc.2",
|
|
65
|
+
"@deepseek-ai/dsh-credentials": "0.1.5-rc.2",
|
|
66
|
+
"@deepseek-ai/dsh-invariants": "0.1.5-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-launch-environment": "0.1.5-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-timeout": "0.1.5-rc.2",
|
|
71
71
|
"@types/node": "^22.0.0",
|
|
72
72
|
"tsdown": "^0.22.0",
|
|
73
|
-
"typescript": "^6.0.0"
|
|
74
|
-
|
|
73
|
+
"typescript": "^6.0.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependenciesMeta": {
|
|
76
|
+
"@deepseek-ai/cordis": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"@deepseek-ai/dsh-anonymous-user-id": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"@deepseek-ai/dsh-attachment": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"@deepseek-ai/dsh-credentials": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"@deepseek-ai/dsh-invariants": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"@deepseek-ai/dsh-launch-environment": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"@deepseek-ai/dsh-llm": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"@deepseek-ai/dsh-settings": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"@deepseek-ai/dsh-timeout": {
|
|
101
|
+
"optional": true
|
|
102
|
+
}
|
|
75
103
|
}
|
|
76
104
|
}
|