dsh-connect-agnes-token-plan 0.5.0
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/CHANGELOG.md +386 -0
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/THIRD_PARTY_NOTICES.md +23 -0
- package/client.js +3921 -0
- package/cordis.patch.yml +147 -0
- package/lib/agnescode-llm-adapter-DDvQ-xvK.js +135 -0
- package/lib/agnescode-models-CUcL9UwK.js +713 -0
- package/lib/host-config-BNGgm_j7.js +456 -0
- package/lib/index.js +7648 -0
- package/lib/llm-adapter-qvvVg7Gb.js +168 -0
- package/lib/llm-error-fix-DQUng8AL.js +247 -0
- package/lib/llm-models-CRYZmwHg.js +1070 -0
- package/lib/raccoon-llm-adapter-n6ZdIdvk.js +143 -0
- package/lib/raccoon-models-DV1sQN4n.js +491 -0
- package/package.json +90 -0
- package/screenshots.json +4 -0
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# dsh-connect-agnes-token-plan — the bundle's own patch layer.
|
|
2
|
+
#
|
|
3
|
+
# Reads the Agnes Token Plan quota through the console API and shows it in
|
|
4
|
+
# a config card on the Plugins page (`plugins.bundle.config` slot) — not in the
|
|
5
|
+
# sidebar. Everything below is plain configuration: edit it here (or
|
|
6
|
+
# override the same id in the profile's cordis.patch.yml) and reinstall/reload.
|
|
7
|
+
#
|
|
8
|
+
# The console token is NOT configured here. Open the panel and sign in once:
|
|
9
|
+
# the Host logs in, keeps the account in the DSH credentials, and re-logs in
|
|
10
|
+
# on its own when the token dies (Agnes issues no refresh token). See README.md.
|
|
11
|
+
- insert:
|
|
12
|
+
- id: dsh-connect-agnes-token-plan
|
|
13
|
+
name: dsh-connect-agnes-token-plan
|
|
14
|
+
config:
|
|
15
|
+
# Console origin (the BACKEND host, not the console front-end: the
|
|
16
|
+
# front-end origin serves /api/* as a Next.js 404 shell). Override if
|
|
17
|
+
# you use a mirror or proxy.
|
|
18
|
+
consoleBase: https://platform-backend.agnes-ai.cn
|
|
19
|
+
# The OpenAI-compatible API base, used only for the optional model
|
|
20
|
+
# catalog (which models this key can call). Leave it alone unless the
|
|
21
|
+
# platform moves the endpoint.
|
|
22
|
+
apiBase: https://api.agnes-ai.cn/v1
|
|
23
|
+
# How many days of usage the chart covers (max 365). Days, not hours:
|
|
24
|
+
# the console's series endpoint takes start_date / end_date as DATES.
|
|
25
|
+
usageDays: 30
|
|
26
|
+
# Pseudo multipliers for the model roster: keys are case-insensitive
|
|
27
|
+
# SUBSTRINGS of a model id (first match in writing order wins), values
|
|
28
|
+
# are positive numbers. Empty by default — the factor used to scale a
|
|
29
|
+
# per-model credit figure, and Agnes publishes no per-model usage at
|
|
30
|
+
# all. What is left is your own annotation: a `×N` badge on a roster
|
|
31
|
+
# row. Rows without a match get no badge, never a guessed 1.
|
|
32
|
+
# Invalid entries (empty key / value <= 0 / non-number) are dropped.
|
|
33
|
+
trendMultipliers: {}
|
|
34
|
+
# How long the Host caches each console response, in seconds.
|
|
35
|
+
cacheSeconds: 60
|
|
36
|
+
# How often the panel asks the Host again, in seconds. The Host states
|
|
37
|
+
# this to the bundle on every snapshot, so the two cannot disagree about
|
|
38
|
+
# how fresh the screen is.
|
|
39
|
+
pollSeconds: 30
|
|
40
|
+
# Deadline for one console call, in milliseconds. The login flow has its
|
|
41
|
+
# own (loginTimeoutMs, below) because it carries a password and may be
|
|
42
|
+
# rate-limited, so the two are not the same number.
|
|
43
|
+
consoleTimeoutMs: 15000
|
|
44
|
+
# Renew the console token this many seconds before it actually expires,
|
|
45
|
+
# so a poll never races the expiry boundary. 120 = two minutes.
|
|
46
|
+
tokenSkewSeconds: 120
|
|
47
|
+
# Extra host names this Host answers as, beyond the loopback defaults
|
|
48
|
+
# (localhost / 127.0.0.1 / ::1). ADDED to them, never substituted — see
|
|
49
|
+
# docs/SETUP.md. Uncomment and list to serve the panel from another name.
|
|
50
|
+
# allowedHosts: []
|
|
51
|
+
# The login flow's overrides. Everything below is optional: a key left
|
|
52
|
+
# commented keeps the platform default. Agnes signs in with ONE request
|
|
53
|
+
# (POST {consoleBase}/api/user/login), so there is no OIDC walk and none
|
|
54
|
+
# of the SenseNova endpoint knobs exist here any more.
|
|
55
|
+
# loginPath: /api/user/login
|
|
56
|
+
# -> the sign-in path, if the platform ever moves it.
|
|
57
|
+
# fallbackExpiresInSeconds: 604800
|
|
58
|
+
# -> lifetime assumed when the access token is not a readable JWT
|
|
59
|
+
# (7 days by default). Deliberately LONG: a token assumed dead
|
|
60
|
+
# while still alive spends a real sign-in on every poll, and Agnes
|
|
61
|
+
# locks an account after a few bad attempts.
|
|
62
|
+
# loginTimeoutMs: 20000
|
|
63
|
+
# -> deadline for one sign-in attempt, in ms. requestTimeoutMs is its
|
|
64
|
+
# older spelling and still wins when only it is set; the console
|
|
65
|
+
# calls use consoleTimeoutMs above.
|
|
66
|
+
# requestTimeoutMs: 20000
|
|
67
|
+
# ------------------------------------------------------------------
|
|
68
|
+
# Vision step two: publish which of this key's models accept image
|
|
69
|
+
# input into this row's OWN settings namespace, for a later LLM
|
|
70
|
+
# connect plugin to read. It writes only its own row - never another
|
|
71
|
+
# provider's imageModelIds - so a miscalculated model list can only
|
|
72
|
+
# affect the panel, not DSH's model routing.
|
|
73
|
+
#
|
|
74
|
+
# `writeImageModelIds: true` -> the Host syncs visionModels into
|
|
75
|
+
# `imageModelIds` on this row's settings on every catalog poll
|
|
76
|
+
# (opt-in, off by default; the panel's read-only info layer is the
|
|
77
|
+
# safe shape).
|
|
78
|
+
# `imageModelIds: []` -> the last written model-id list.
|
|
79
|
+
# `visionModels: []` -> the last full vision identification
|
|
80
|
+
# (ids + source markers), kept for a reader that wants the detail.
|
|
81
|
+
writeImageModelIds: false
|
|
82
|
+
imageModelIds: []
|
|
83
|
+
visionModels: []
|
|
84
|
+
# ------------------------------------------------------------------
|
|
85
|
+
# Step three ("one-stop service"): register the Agnes LLM
|
|
86
|
+
# provider DIRECTLY, with no hand-written llm-pi-ai row. When true
|
|
87
|
+
# the Host offers an OpenAI-compatible provider
|
|
88
|
+
# (`agnes-token-plan`, base apiBase above): the panel's API-key
|
|
89
|
+
# field stores the `sk-` key in the DSH credentials (the process
|
|
90
|
+
# environment still works as a fallback), the catalog poll builds
|
|
91
|
+
# the model list, and vision models accept image input
|
|
92
|
+
# automatically. The cached catalog and the model allow-list live
|
|
93
|
+
# in this plugin's own state file, never on this row. Off by
|
|
94
|
+
# default - registering a model source is a Host-wide change.
|
|
95
|
+
# This value is now the DEPLOYMENT DEFAULT only: the panel's
|
|
96
|
+
#「模型接入」switch (POST /api/<name>/provider) overrides it live,
|
|
97
|
+
# with no restart. See docs/PROVIDER-HOT-RELOAD.md.
|
|
98
|
+
registerProvider: false
|
|
99
|
+
# ------------------------------------------------------------------
|
|
100
|
+
# Draw absorption (docs/ARCHITECTURE.md §5.4, route B): when true AND
|
|
101
|
+
# the Host exposes a tools service, the agent gains an
|
|
102
|
+
# `agnes_draw_image` tool that POSTs to
|
|
103
|
+
# `{apiBase}/images/generations` with the panel-saved
|
|
104
|
+
# `AGNES_TOKEN_PLAN_API_KEY`. Draw models are identified by the
|
|
105
|
+
# catalog's own `output_modalities` when the platform declares it and
|
|
106
|
+
# by an `image` segment in the model id when it does not - the Agnes
|
|
107
|
+
# gateway declares NO modality field at all (docs/AGNES-API.md §7.1),
|
|
108
|
+
# so the name fallback is the path that actually fires here. The key is
|
|
109
|
+
# resolved per call, and a failed draw starts a 30s cooldown. Off by
|
|
110
|
+
# default - an agent tool is a Host-wide change; a Host without the
|
|
111
|
+
# tools service simply never sees it.
|
|
112
|
+
# drawEnabled: false
|
|
113
|
+
# -> the opt-in switch (strict boolean).
|
|
114
|
+
# drawModelId: ""
|
|
115
|
+
# -> preferred image model id; empty = first of the catalog
|
|
116
|
+
# (e.g. Agnes-u1-fast).
|
|
117
|
+
# drawTimeoutMs: 120000
|
|
118
|
+
# -> deadline for one image request (image models are slow).
|
|
119
|
+
# ------------------------------------------------------------------
|
|
120
|
+
# ------------------------------------------------------------------
|
|
121
|
+
# Video absorption (docs/AGNES-API.md §7.5): the same ladder as draw,
|
|
122
|
+
# with one protocol difference that decides the shape - video is an
|
|
123
|
+
# ASYNCHRONOUS TASK, so the tool creates a task and polls it. Off by
|
|
124
|
+
# default, like the draw tool.
|
|
125
|
+
# videoEnabled: false
|
|
126
|
+
# -> the opt-in switch (strict boolean).
|
|
127
|
+
# videoModelId: ""
|
|
128
|
+
# -> preferred video model id; empty = first V2.0-family model in the
|
|
129
|
+
# catalog. The 2.5 family (`agnes-video-2.5`, `-2.5-flash`) is
|
|
130
|
+
# NOT selectable: it takes `mode`/`seconds`/`size`/`aspect_ratio`
|
|
131
|
+
# instead of `width`/`height`/`num_frames`/`frame_rate`, and
|
|
132
|
+
# mixing the two schemas is a 400. The panel names the excluded
|
|
133
|
+
# models so their absence is explained rather than mysterious.
|
|
134
|
+
# videoTimeoutMs: 600000
|
|
135
|
+
# -> deadline for the WHOLE task (create + poll). Minutes, not
|
|
136
|
+
# seconds; floor 30000.
|
|
137
|
+
# videoWidth: 1152
|
|
138
|
+
# videoHeight: 768
|
|
139
|
+
# -> the V2.0 frame size. Passed straight through.
|
|
140
|
+
# videoNumFrames: 121
|
|
141
|
+
# -> frame count; must be <= 441 AND satisfy 8n+1 (81 / 121 / 161 /
|
|
142
|
+
# 241 / 441). 121 @ 24fps is about 5 seconds. An illegal explicit
|
|
143
|
+
# value is REFUSED, not clamped: silently shortening a clip the
|
|
144
|
+
# caller asked for is worse than an error they can read.
|
|
145
|
+
# videoFrameRate: 24
|
|
146
|
+
# -> frames per second, 1-60.
|
|
147
|
+
# ------------------------------------------------------------------
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { a as name } from "./host-config-BNGgm_j7.js";
|
|
2
|
+
import { i as buildAgnescodeDescriptors, n as AGNESCODE_PROVIDER_ID, r as agnescodeRoster, t as AGNESCODE_DISPLAY_NAME } from "./agnescode-models-CUcL9UwK.js";
|
|
3
|
+
import { n as buildRetryPolicyConfig, t as reclassifyStream } from "./llm-error-fix-DQUng8AL.js";
|
|
4
|
+
import { createProvider } from "@earendil-works/pi-ai";
|
|
5
|
+
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
6
|
+
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
7
|
+
import { resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
8
|
+
|
|
9
|
+
//#region src/host/agnescode-llm-adapter.ts
|
|
10
|
+
/**
|
|
11
|
+
* The peer-dependent half of the directly-registered AgnesCode provider —
|
|
12
|
+
* "third upstream provider" (ROADMAP §6.3).
|
|
13
|
+
*
|
|
14
|
+
* Mirrors `raccoon-llm-adapter.ts`'s shape: ONE `PiAiAdapter` carrying ONE
|
|
15
|
+
* profile (`sensenova-agnescode` → the credential's per-account BFF base), an
|
|
16
|
+
* INERT pi-ai auth plane (the AgnesCode JWT is resolved per request from the
|
|
17
|
+
* plugin's own credential store, pi-ai never manufactures it), and no token
|
|
18
|
+
* baked into the profile — the picker advertises models without one and a
|
|
19
|
+
* request fails at resolve time, where the panel status is visible.
|
|
20
|
+
*
|
|
21
|
+
* IMAGE hooks are deliberately NOT wired in v1: every catalogue row declares
|
|
22
|
+
* `model_type: "text"` with no image modality fact (ROADMAP §6.3), so there
|
|
23
|
+
* is nothing honest to advertise — a vision answer here would be an invented
|
|
24
|
+
* fact. If the platform later declares image input, this is the one place to
|
|
25
|
+
* add the hooks (mirror `raccoon-llm-adapter.ts`).
|
|
26
|
+
*
|
|
27
|
+
* The descriptor mapping lives in the peer-free `agnescode-models.ts`, so this
|
|
28
|
+
* module holds only assembly against the runtime and is exercised by the
|
|
29
|
+
* wiring/e2e checks, exactly as `llm-adapter.ts` is.
|
|
30
|
+
*
|
|
31
|
+
* @module dsh-connect-agnes-token-plan/agnescode-llm-adapter
|
|
32
|
+
*/
|
|
33
|
+
/** Idle ceiling while one stream read is outstanding (dsh-llm-pi-ai default). */
|
|
34
|
+
const STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
35
|
+
/**
|
|
36
|
+
* Inert pi-ai auth plane.
|
|
37
|
+
*
|
|
38
|
+
* Authentication goes through `resolveApiKey` (the stored AgnesCode JWT, read
|
|
39
|
+
* per request from `agnescode-store.ts`) — pi-ai's own credential lifecycle
|
|
40
|
+
* must never manufacture a credential for this route, so every ambient
|
|
41
|
+
* question answers "nothing stored, nothing set".
|
|
42
|
+
*/
|
|
43
|
+
const INERT_AUTH = {
|
|
44
|
+
credentials: {
|
|
45
|
+
async read() {},
|
|
46
|
+
async list() {
|
|
47
|
+
return [];
|
|
48
|
+
},
|
|
49
|
+
async modify() {},
|
|
50
|
+
async delete() {}
|
|
51
|
+
},
|
|
52
|
+
authContext: {
|
|
53
|
+
async env() {},
|
|
54
|
+
async fileExists() {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Assemble the AgnesCode adapter for one credential/catalogue snapshot.
|
|
61
|
+
*
|
|
62
|
+
* A fresh instance per rebuild is deliberate: `PiAiAdapter` memoizes the
|
|
63
|
+
* profiles snapshot internally, so the caller REPLACES the registered
|
|
64
|
+
* adapter when the credential or catalogue changes and emits
|
|
65
|
+
* `llm/adapters-updated`.
|
|
66
|
+
* @param {object} options - wiring.
|
|
67
|
+
* @param {object[]} [options.rows] - the AgnesCode roster rows (`agnescodeRoster`).
|
|
68
|
+
* @param {string} options.bffBase - the credential's pinned per-account BFF base.
|
|
69
|
+
* @param {() => Promise<string>} options.resolveToken - resolves the live
|
|
70
|
+
* AgnesCode JWT per request (re-harvested by the caller when expired).
|
|
71
|
+
* @param {(service: string) => unknown} [options.get] - service resolver
|
|
72
|
+
* (reserved for the image hooks when the platform declares image input).
|
|
73
|
+
* @returns {{adapter: object, providerIds: string[]}} the adapter and the ids
|
|
74
|
+
* it owns.
|
|
75
|
+
*/
|
|
76
|
+
function createAgnescodeAdapter({ rows, bffBase, resolveToken, get } = {}) {
|
|
77
|
+
const models = buildAgnescodeDescriptors(rows ?? agnescodeRoster(null), { bffBase: String(bffBase ?? "") });
|
|
78
|
+
const provider = {
|
|
79
|
+
...createProvider({
|
|
80
|
+
id: AGNESCODE_PROVIDER_ID,
|
|
81
|
+
name: AGNESCODE_DISPLAY_NAME,
|
|
82
|
+
auth: { apiKey: {
|
|
83
|
+
name: "AgnesCode access token",
|
|
84
|
+
async resolve({ credential } = {}) {
|
|
85
|
+
const token = credential?.key;
|
|
86
|
+
return token === void 0 || token === "" ? void 0 : {
|
|
87
|
+
auth: { apiKey: token },
|
|
88
|
+
source: AGNESCODE_DISPLAY_NAME
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
} },
|
|
92
|
+
models,
|
|
93
|
+
api: openAICompletionsApi()
|
|
94
|
+
}),
|
|
95
|
+
getModels: () => models
|
|
96
|
+
};
|
|
97
|
+
const profiles = /* @__PURE__ */ new Map([[AGNESCODE_PROVIDER_ID, {
|
|
98
|
+
provider: AGNESCODE_PROVIDER_ID,
|
|
99
|
+
displayName: AGNESCODE_DISPLAY_NAME,
|
|
100
|
+
streamIdleTimeoutMs: STREAM_IDLE_TIMEOUT_MS,
|
|
101
|
+
retryPolicy: resolveRetryPolicy(buildRetryPolicyConfig(), `${name}.${AGNESCODE_PROVIDER_ID}.retryPolicy`),
|
|
102
|
+
configuredMaxTokens: /* @__PURE__ */ new Map(),
|
|
103
|
+
modelErrors: /* @__PURE__ */ new Map(),
|
|
104
|
+
piProvider: provider
|
|
105
|
+
}]]);
|
|
106
|
+
const inner = new PiAiAdapter({
|
|
107
|
+
profiles: () => profiles,
|
|
108
|
+
auth: INERT_AUTH,
|
|
109
|
+
resolveApiKey: async () => resolveToken(),
|
|
110
|
+
resolveAttachments: () => get?.("attachments"),
|
|
111
|
+
resolveImageAccess: void 0
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
adapter: new Proxy(inner, { get(target, prop, receiver) {
|
|
115
|
+
const value = Reflect.get(target, prop, receiver);
|
|
116
|
+
if (prop === "stream") return (options) => reclassifyStream(target.stream(options));
|
|
117
|
+
if (typeof value === "function" && prop === "prepareCall") return (...args) => {
|
|
118
|
+
const prepared = value.apply(target, args);
|
|
119
|
+
if (prepared && typeof prepared.then === "function") return prepared.then((p) => p && typeof p.stream === "function" ? {
|
|
120
|
+
...p,
|
|
121
|
+
stream: (o) => reclassifyStream(p.stream(o))
|
|
122
|
+
} : p);
|
|
123
|
+
return prepared && typeof prepared.stream === "function" ? {
|
|
124
|
+
...prepared,
|
|
125
|
+
stream: (o) => reclassifyStream(prepared.stream(o))
|
|
126
|
+
} : prepared;
|
|
127
|
+
};
|
|
128
|
+
return value;
|
|
129
|
+
} }),
|
|
130
|
+
providerIds: [AGNESCODE_PROVIDER_ID]
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
export { createAgnescodeAdapter };
|