@siliconflow-official/dsh-llm-siliconflow 0.2.0-rc.2 → 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 +11 -1
- package/lib/bin.js +23 -22
- 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 +16 -4
- package/lib/{types-iMqgaeX4.js → types-IqSaZRz4.js} +149 -6
- package/package.json +38 -11
package/README.md
CHANGED
|
@@ -34,11 +34,21 @@ dsh-siliconflow-setup
|
|
|
34
34
|
export SILICONFLOW_API_KEY=sk-... # 或写入 $DSH_HOME/.credentials.yaml
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
写入 `.credentials.yaml`
|
|
37
|
+
写入 `.credentials.yaml` 采用**跨代兼容规则**(单一构建同时服务新旧两代 dsh):
|
|
38
|
+
|
|
39
|
+
- **新文件 / 扁平(无 `version`)文件 → 保持扁平写入**:旧代 dsh(credentials-local 0.1.0)原生可读;新代 dsh 会在下次启动时自动迁移为 `version: 1`。
|
|
40
|
+
- **已是 `version: 1` 的文件 → 保持 version-1 写入**:新代用户永不降级;新旧两代读路径都识别这两种布局。
|
|
41
|
+
- 其他无法识别的文档(未知键、非法结构)会明确报错而不是被改写;早期版本向导误写的顶层键会在写入时折回 `refs:`。
|
|
38
42
|
|
|
39
43
|
```yaml
|
|
44
|
+
# 向导写出的新文件(扁平,两代公共分母):
|
|
45
|
+
OTHER_KEY: sk-...
|
|
46
|
+
SILICONFLOW_API_KEY: sk-...
|
|
47
|
+
|
|
48
|
+
# 新代 dsh 首次启动后自动迁移为:
|
|
40
49
|
version: 1
|
|
41
50
|
refs:
|
|
51
|
+
OTHER_KEY: sk-...
|
|
42
52
|
SILICONFLOW_API_KEY: sk-...
|
|
43
53
|
```
|
|
44
54
|
|
package/lib/bin.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { dirname, join, resolve } from "node:path";
|
|
8
|
-
import {
|
|
8
|
+
import { parseDocument } from "yaml";
|
|
9
9
|
//#region lib/types/setup.js
|
|
10
10
|
/**
|
|
11
11
|
* Interactive setup wizard core for {@link @siliconflow-official/dsh-llm-siliconflow}:
|
|
@@ -100,21 +100,6 @@ function admitCredentialsDocument(document) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
|
-
* Nest a pre-release flat document under `refs:` with a `version` stamp. The
|
|
104
|
-
* whole current root — every flat entry, comments included — becomes the
|
|
105
|
-
* `refs` section verbatim, so every other provider's key migrates in the same
|
|
106
|
-
* write; the new root carries `version` and `refs` in that order, matching how
|
|
107
|
-
* `dsh-credentials-local` writes one from scratch.
|
|
108
|
-
* @param document - the admitted flat document to upgrade in place.
|
|
109
|
-
*/
|
|
110
|
-
function upgradeFlatDocument(document) {
|
|
111
|
-
const refs = document.contents instanceof YAMLMap ? document.contents : new YAMLMap();
|
|
112
|
-
const root = new YAMLMap();
|
|
113
|
-
root.add(new Pair("version", CREDENTIALS_LAYOUT_VERSION));
|
|
114
|
-
root.add(new Pair("refs", refs));
|
|
115
|
-
document.contents = root;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
103
|
* Read one credential reference from a versioned or pre-release flat document.
|
|
119
104
|
* @param path - the credentials document path.
|
|
120
105
|
* @param keyEnv - the reference name to read (e.g. `SILICONFLOW_API_KEY`).
|
|
@@ -134,10 +119,22 @@ async function readCredential(path, keyEnv) {
|
|
|
134
119
|
return admitted.entries.get(keyEnv);
|
|
135
120
|
}
|
|
136
121
|
/**
|
|
137
|
-
* Write one credential reference
|
|
138
|
-
* other entry and comment.
|
|
139
|
-
*
|
|
140
|
-
*
|
|
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
|
|
141
138
|
* corrupted. An unrecognized document fails loud instead of being rewritten —
|
|
142
139
|
* a silent rewrite would hide why the running harness rejects it.
|
|
143
140
|
* @param path - the credentials document path; created when absent.
|
|
@@ -149,7 +146,11 @@ async function writeCredential(path, keyEnv, key) {
|
|
|
149
146
|
const doc = await loadDocument(path);
|
|
150
147
|
const admitted = admitCredentialsDocument(doc);
|
|
151
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`);
|
|
152
|
-
if (admitted.kind === "flat")
|
|
149
|
+
if (admitted.kind === "flat") {
|
|
150
|
+
doc.setIn([keyEnv], key);
|
|
151
|
+
await persistDocument(path, doc);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
153
154
|
for (const extra of admitted.extraKeys?.keys() ?? []) doc.deleteIn([extra]);
|
|
154
155
|
for (const [extra, value] of admitted.extraKeys ?? /* @__PURE__ */ new Map()) doc.setIn(["refs", extra], value);
|
|
155
156
|
doc.setIn(["version"], CREDENTIALS_LAYOUT_VERSION);
|
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
|
@@ -64,10 +64,22 @@ export declare function settingsPath(home: string): string;
|
|
|
64
64
|
*/
|
|
65
65
|
export declare function readCredential(path: string, keyEnv: string): Promise<string | undefined>;
|
|
66
66
|
/**
|
|
67
|
-
* Write one credential reference
|
|
68
|
-
* other entry and comment.
|
|
69
|
-
*
|
|
70
|
-
*
|
|
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
|
|
71
83
|
* corrupted. An unrecognized document fails loud instead of being rewritten —
|
|
72
84
|
* a silent rewrite would hide why the running harness rejects it.
|
|
73
85
|
* @param path - the credentials document path; created when absent.
|
|
@@ -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
|
};
|
|
@@ -1126,7 +1234,11 @@ function apply(ctx, config) {
|
|
|
1126
1234
|
registeredPolicy = policy;
|
|
1127
1235
|
};
|
|
1128
1236
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
1129
|
-
settingsCtx.settings
|
|
1237
|
+
assembleSettingsSection(settingsCtx.settings, {
|
|
1238
|
+
owner: ctx,
|
|
1239
|
+
ns: NS,
|
|
1240
|
+
schema: Config,
|
|
1241
|
+
entry: config,
|
|
1130
1242
|
setSource: (source) => {
|
|
1131
1243
|
current = source;
|
|
1132
1244
|
},
|
|
@@ -1134,5 +1246,36 @@ function apply(ctx, config) {
|
|
|
1134
1246
|
});
|
|
1135
1247
|
});
|
|
1136
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
|
+
}
|
|
1137
1280
|
//#endregion
|
|
1138
|
-
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,15 +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": "
|
|
55
|
-
"@deepseek-ai/dsh-util-values": "*"
|
|
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": "*"
|
|
56
55
|
},
|
|
57
56
|
"dependencies": {
|
|
58
57
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
@@ -69,9 +68,37 @@
|
|
|
69
68
|
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
70
69
|
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
71
70
|
"@deepseek-ai/dsh-timeout": "0.1.5-rc.2",
|
|
72
|
-
"@deepseek-ai/dsh-util-values": "0.1.5-rc.2",
|
|
73
71
|
"@types/node": "^22.0.0",
|
|
74
72
|
"tsdown": "^0.22.0",
|
|
75
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
|
+
}
|
|
76
103
|
}
|
|
77
104
|
}
|