@mokuyoaxis/dsh-iris 0.3.0-alpha.1 → 0.3.0-beta.1
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 +63 -1
- package/README.en.md +20 -13
- package/README.md +20 -13
- package/bin/dsh-iris.js +143 -51
- package/docs/AGENT_CORE_QUICKSTART.md +2 -2
- package/docs/AGENT_QUICKSTART.md +1 -1
- package/docs/ARCHITECTURE.md +5 -5
- package/docs/ARTIFACT_MANIFEST.md +4 -0
- package/docs/AUDIO_FLOWS.md +20 -4
- package/docs/BETA_SCOPE.md +42 -0
- package/docs/CONTACT_SHEET.md +1 -1
- package/docs/CORE_CONNECTIONS.md +3 -1
- package/docs/CORE_QUICKSTART.md +7 -5
- package/docs/CORE_RUNTIME_CONTRACT.md +3 -3
- package/docs/CORE_SERVICE.md +9 -5
- package/docs/CORE_SKILLS.md +2 -2
- package/docs/DSH_COMPATIBILITY.md +11 -1
- package/docs/DSH_CORE_MIGRATION.md +6 -0
- package/docs/DSH_RUNTIME.md +2 -2
- package/docs/HEADLESS_CLI.md +29 -4
- package/docs/HOST_ADAPTER_CONTRACT.md +5 -3
- package/docs/IMAGE_FLOW.md +2 -2
- package/docs/IMAGE_GENERATION.md +1 -1
- package/docs/LOCAL_API.md +2 -2
- package/docs/LOCAL_API_CONTRACT.md +17 -15
- package/docs/LOCAL_CREATION.md +2 -2
- package/docs/LOCAL_OPERATIONS.md +2 -2
- package/docs/LOCAL_SELECTION.md +1 -1
- package/docs/LOCAL_WEB.md +11 -5
- package/docs/MCP.md +30 -9
- package/docs/MEDIA_SCHEDULING.md +72 -0
- package/docs/NETWORK_RESOURCES.md +106 -0
- package/docs/NETWORK_ROUTING.md +103 -0
- package/docs/NETWORK_WORKBENCH.md +65 -0
- package/docs/PANEL_EXTENSIONS.md +1 -1
- package/docs/PLUGINS.md +9 -5
- package/docs/PLUGIN_DEVELOPMENT.md +39 -0
- package/docs/PLUGIN_STARTUP.md +15 -2
- package/docs/PROVIDER_ADAPTER_CONTRACT.md +8 -2
- package/docs/PROVIDER_CREDENTIALS.md +67 -0
- package/docs/PROVIDER_EXTENSIONS.md +8 -4
- package/docs/PROVIDER_HEALTH.md +4 -2
- package/docs/PROVIDER_NETWORK.md +45 -0
- package/docs/PROVIDER_PLUGIN_CONTRACT.md +24 -0
- package/docs/PROVIDER_SETTINGS.md +13 -7
- package/docs/PROVIDER_SUBMISSION_CONTRACT.md +3 -1
- package/docs/ROADMAP.md +6 -6
- package/docs/SECURITY.md +1 -1
- package/docs/SYNC_DELIVERY_RECOVERY.md +5 -1
- package/docs/VIDEO_FLOWS.md +89 -0
- package/docs/VISION_MODEL.md +2 -0
- package/docs/WEB_PROTOTYPE.md +3 -3
- package/docs/WEB_RECOVERY.md +2 -2
- package/docs/WORKBENCH_ARTIFACTS.md +6 -0
- package/docs/WORKBENCH_LOGIN.md +2 -2
- package/docs/releases/0.3.0-beta.1.md +34 -0
- package/docs/screenshots.md +2 -2
- package/lib/actions.js +5 -4
- package/lib/adapters.js +58 -41
- package/lib/api.js +23 -13
- package/lib/application-runtime.js +44 -15
- package/lib/application-writer.js +193 -0
- package/lib/client.d.ts +3 -3
- package/lib/client.js +62 -25
- package/lib/comfyui-adapter.js +3 -2
- package/lib/comfyui-client.js +10 -1
- package/lib/command-service.js +18 -5
- package/lib/config.js +13 -9
- package/lib/configured-image-runtime.js +28 -9
- package/lib/connection-skill.js +9 -0
- package/lib/core-artifact-store.js +68 -26
- package/lib/core-bridge-tools.js +5 -3
- package/lib/core-capability-catalog.js +21 -11
- package/lib/core-delivery-cache.js +8 -3
- package/lib/core-service-client.js +6 -2
- package/lib/core-tasks.js +86 -11
- package/lib/creation-request-store.js +8 -5
- package/lib/doctor.js +2 -2
- package/lib/dsh-core-adapter.js +61 -9
- package/lib/dsh-provider-config.js +1 -1
- package/lib/dsh-service-bridge.js +1 -0
- package/lib/flow-store.js +64 -17
- package/lib/image-flow.js +39 -17
- package/lib/index.js +10 -5
- package/lib/local-api-projection.js +6 -2
- package/lib/local-api.js +137 -49
- package/lib/local-creation.js +12 -5
- package/lib/local-operations.js +2 -2
- package/lib/local-request-root.js +2 -2
- package/lib/local-web-assets.js +2 -0
- package/lib/mcp-http.js +82 -9
- package/lib/mcp-server.js +23 -1
- package/lib/media-scheduler.js +194 -0
- package/lib/model-directory-service.js +25 -8
- package/lib/music-transport.js +4 -2
- package/lib/network-config-service.js +123 -0
- package/lib/network-config.js +127 -0
- package/lib/network-doctor.js +94 -0
- package/lib/network-management.js +51 -0
- package/lib/network-policy.js +19 -0
- package/lib/network-route-evidence.js +38 -0
- package/lib/network-routing.js +283 -0
- package/lib/operation-runner.js +83 -38
- package/lib/operation-store.js +6 -6
- package/lib/plugins.d.ts +25 -6
- package/lib/plugins.js +2 -1
- package/lib/provider-adapter.js +2 -2
- package/lib/provider-adapters.js +37 -5
- package/lib/provider-catalog.js +33 -16
- package/lib/provider-config-format.js +51 -0
- package/lib/provider-config-service.js +52 -22
- package/lib/provider-context.d.ts +90 -0
- package/lib/provider-context.js +42 -0
- package/lib/provider-credentials.js +97 -0
- package/lib/provider-diagnostics.js +5 -5
- package/lib/provider-health.js +10 -5
- package/lib/provider-monitor.js +5 -3
- package/lib/provider-network.js +52 -0
- package/lib/provider-protocol.js +3 -2
- package/lib/provider-registry.js +15 -5
- package/lib/provider-response.js +10 -0
- package/lib/provider-settings.js +14 -12
- package/lib/provider-task-runner.js +113 -37
- package/lib/provider-transport.js +16 -0
- package/lib/render.js +11 -1
- package/lib/scheduling-policy.js +66 -0
- package/lib/tasks.js +3 -2
- package/lib/video-flow-input.js +16 -0
- package/lib/video-input.js +12 -5
- package/lib/vision-model-routing.js +2 -0
- package/lib/web/app.js +107 -46
- package/lib/web/connections.js +151 -12
- package/lib/web/creation.js +29 -25
- package/lib/web/flow-view.js +4 -3
- package/lib/web/gallery.js +17 -16
- package/lib/web/index.html +58 -5
- package/lib/web/media-client.js +18 -4
- package/lib/web/network.js +232 -0
- package/lib/web/plugin-panel.js +4 -2
- package/lib/web/plugin-status.js +140 -0
- package/lib/web/providers.js +34 -13
- package/lib/web/recovery.js +2 -2
- package/lib/web/styles.css +54 -1
- package/lib/web/transport.js +6 -1
- package/lib/workbench-artifacts.js +3 -2
- package/package.json +5 -2
- package/skills/iris-core/references/comfyui.md +1 -1
- package/templates/plugin-starter/LICENSE +21 -0
- package/templates/plugin-starter/README.md +74 -0
- package/templates/plugin-starter/assets/peach.png +0 -0
- package/templates/plugin-starter/assets/violet.png +0 -0
- package/templates/plugin-starter/index.mjs +27 -0
- package/templates/plugin-starter/package.json +18 -0
- package/templates/plugin-starter/scripts/core-cli.mjs +20 -0
- package/templates/plugin-starter/scripts/environment.mjs +65 -0
- package/templates/plugin-starter/scripts/mcp-probe.mjs +22 -0
- package/templates/plugin-starter/scripts/setup.mjs +42 -0
- package/templates/plugin-starter/scripts/smoke.mjs +76 -0
- package/templates/plugin-starter/web/board.css +27 -0
- package/templates/plugin-starter/web/board.mjs +160 -0
- package/templates/plugin-starter/web/png.mjs +23 -0
- package/templates/provider-starter/LICENSE +21 -0
- package/templates/provider-starter/README.md +50 -0
- package/templates/provider-starter/index.d.mts +5 -0
- package/templates/provider-starter/index.mjs +165 -0
- package/templates/provider-starter/package.json +13 -0
- package/templates/provider-starter/scripts/core-cli.mjs +22 -0
- package/templates/provider-starter/scripts/environment.mjs +122 -0
- package/templates/provider-starter/scripts/fixture.mjs +54 -0
- package/templates/provider-starter/scripts/mcp-probe.mjs +21 -0
- package/templates/provider-starter/scripts/protocol-smoke.mjs +48 -0
- package/templates/provider-starter/scripts/setup.mjs +44 -0
- package/templates/provider-starter/scripts/smoke.mjs +145 -0
- package/user_guide.md +3 -3
package/lib/render.js
CHANGED
|
@@ -22,6 +22,7 @@ import { imageDimensions } from './pixels.js';
|
|
|
22
22
|
|
|
23
23
|
const PNG_DATA_PREFIX = 'data:image/png;base64,';
|
|
24
24
|
const MAX_SCREENSHOT_BYTES = 64 * 1024 * 1024;
|
|
25
|
+
const pendingRenders = new Map();
|
|
25
26
|
|
|
26
27
|
async function screenshotPng(result) {
|
|
27
28
|
const dataUrl = result?.dataUrl;
|
|
@@ -127,6 +128,8 @@ export class BrowserHtmlRenderer {
|
|
|
127
128
|
const htmlPath = path.join(dir, 'index.html');
|
|
128
129
|
writePrivateFile(htmlPath, content);
|
|
129
130
|
const url = `${address.baseUrl}/iris/render/${path.basename(dir)}/index.html`;
|
|
131
|
+
const delivery = { served: false };
|
|
132
|
+
pendingRenders.set(path.basename(dir), delivery);
|
|
130
133
|
try {
|
|
131
134
|
// open 只接受可选的会话名称;取消发生在打开期间时,取得会话后仍须关闭。
|
|
132
135
|
const session = await b.open();
|
|
@@ -136,6 +139,9 @@ export class BrowserHtmlRenderer {
|
|
|
136
139
|
throwIfAborted(signal);
|
|
137
140
|
await waitForRender(this.waitMs, signal); // 等页面加载(无加载完成事件可用的折中)
|
|
138
141
|
throwIfAborted(signal);
|
|
142
|
+
if (!delivery.served) throw Object.assign(new RenderError('未确认 Iris 已返回本次 HTML 页面;请检查渲染路由和 DSH_WEB_BASE。'), {
|
|
143
|
+
code: 'IRIS_RENDER_ROUTE_UNCONFIRMED'
|
|
144
|
+
});
|
|
139
145
|
// 不要求浏览器写入 Iris 私有目录,兼容 downloadDir 内的保存路径限制。
|
|
140
146
|
const result = await b.screenshot(session, { fullPage: fullPage !== false, format: 'png' }, signal);
|
|
141
147
|
throwIfAborted(signal);
|
|
@@ -146,6 +152,7 @@ export class BrowserHtmlRenderer {
|
|
|
146
152
|
try { await b.close(session); } catch (_) { /* 幂等 */ }
|
|
147
153
|
}
|
|
148
154
|
} finally {
|
|
155
|
+
pendingRenders.delete(path.basename(dir));
|
|
149
156
|
fs.rmSync(dir, { recursive: true, force: true }); // 用完即清
|
|
150
157
|
}
|
|
151
158
|
}
|
|
@@ -196,7 +203,10 @@ export function serveRender(req, res) {
|
|
|
196
203
|
'Referrer-Policy': 'no-referrer',
|
|
197
204
|
'X-Content-Type-Options': 'nosniff'
|
|
198
205
|
});
|
|
199
|
-
|
|
206
|
+
const body = method === 'HEAD' ? undefined : fs.readFileSync(abs);
|
|
207
|
+
const delivery = file === 'index.html' && method === 'GET' ? pendingRenders.get(subdir) : undefined;
|
|
208
|
+
if (delivery) res.once('finish', () => { delivery.served = true; });
|
|
209
|
+
res.end(body);
|
|
200
210
|
} catch (err) {
|
|
201
211
|
if (!res.headersSent) res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
202
212
|
res.end('render error');
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Explicit startup policy; never discovers files or rewrites existing configuration.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export function schedulingError(code, message) {
|
|
6
|
+
return Object.assign(new Error(message), { code, stage: 'prepare', category: 'rate_limit', acceptance: 'not_accepted' });
|
|
7
|
+
}
|
|
8
|
+
const invalid = message => { throw schedulingError('IRIS_SCHEDULER_CONFIG_INVALID', message); };
|
|
9
|
+
function fields(value, names, where) {
|
|
10
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).some(name => !names.includes(name))) invalid(`${where} 包含无效字段`);
|
|
11
|
+
}
|
|
12
|
+
function integer(value, min, max, name) {
|
|
13
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) invalid(`${name} 需要 ${min}–${max} 的整数`);
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function identity(value, where) {
|
|
17
|
+
if (typeof value !== 'string' || !value.trim() || value !== value.trim() || value.length > 256 || /[\x00-\x1f\x7f]/.test(value)) invalid(`${where} 无效`);
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function limits(value, defaults = {}) {
|
|
21
|
+
return Object.fromEntries([
|
|
22
|
+
['httpConcurrency', 1, 4], ['requestsPerSecond', 1, 1000], ['remoteLimit', 1, 1000]
|
|
23
|
+
].flatMap(([key, min, max]) => value[key] === undefined && defaults[key] === undefined ? []
|
|
24
|
+
: [[key, integer(value[key] === undefined ? defaults[key] : value[key], min, max, key)]]));
|
|
25
|
+
}
|
|
26
|
+
export function checkedSchedulingPolicy(input) {
|
|
27
|
+
fields(input, ['schemaVersion', 'httpConcurrency', 'requestsPerSecond', 'remoteLimit', 'queueLimit', 'queueTimeoutMs', 'providers'], '调度策略');
|
|
28
|
+
if (input.schemaVersion !== 1) invalid('仅支持 scheduler schemaVersion=1;不会忽略或迁移其他版本');
|
|
29
|
+
const providers = input.providers === undefined ? [] : input.providers;
|
|
30
|
+
if (!Array.isArray(providers) || providers.length > 128) invalid('providers 需要至多 128 项的数组');
|
|
31
|
+
const seen = new Set();
|
|
32
|
+
return Object.freeze({ schemaVersion: 1, ...limits(input, { httpConcurrency: 4, requestsPerSecond: 2, remoteLimit: 8 }),
|
|
33
|
+
queueLimit: integer(input.queueLimit === undefined ? 32 : input.queueLimit, 8, 128, 'queueLimit'),
|
|
34
|
+
queueTimeoutMs: integer(input.queueTimeoutMs === undefined ? 30000 : input.queueTimeoutMs, 1, 300000, 'queueTimeoutMs'),
|
|
35
|
+
providers: Object.freeze(providers.map(provider => {
|
|
36
|
+
fields(provider, ['providerId', 'httpConcurrency', 'requestsPerSecond', 'remoteLimit', 'models'], '供应商策略');
|
|
37
|
+
const providerId = identity(provider.providerId, 'providerId');
|
|
38
|
+
if (seen.has(providerId)) invalid('providerId 重复'); seen.add(providerId);
|
|
39
|
+
const models = provider.models === undefined ? [] : provider.models, ids = new Set();
|
|
40
|
+
if (!Array.isArray(models) || models.length > 256) invalid('models 需要至多 256 项的数组');
|
|
41
|
+
return Object.freeze({ providerId, ...limits(provider), models: Object.freeze(models.map(model => {
|
|
42
|
+
fields(model, ['modelId', 'httpConcurrency', 'requestsPerSecond', 'remoteLimit'], '模型策略');
|
|
43
|
+
const modelId = identity(model.modelId, 'modelId');
|
|
44
|
+
if (ids.has(modelId)) invalid('modelId 重复'); ids.add(modelId);
|
|
45
|
+
return Object.freeze({ modelId, ...limits(model) });
|
|
46
|
+
})) });
|
|
47
|
+
})) });
|
|
48
|
+
}
|
|
49
|
+
export function readSchedulingPolicy(file) {
|
|
50
|
+
if (typeof file !== 'string' || !path.isAbsolute(file)) invalid('scheduler-config 需要明确的绝对路径');
|
|
51
|
+
let fd;
|
|
52
|
+
try {
|
|
53
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
54
|
+
const stat = fs.fstatSync(fd);
|
|
55
|
+
if (!stat.isFile() || stat.size > 262144) invalid('scheduler-config 必须是 ≤256 KiB 的普通文件');
|
|
56
|
+
if (process.platform !== 'win32' && (stat.mode & 0o077)) invalid('scheduler-config 请限制为仅当前用户可读写');
|
|
57
|
+
const bytes = fs.readFileSync(fd);
|
|
58
|
+
if (bytes.length > 262144) invalid('scheduler-config 过大');
|
|
59
|
+
let value;
|
|
60
|
+
try { value = JSON.parse(bytes.toString('utf8')); } catch { invalid('scheduler-config 不是有效 JSON'); }
|
|
61
|
+
return checkedSchedulingPolicy(value);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error.code === 'IRIS_SCHEDULER_CONFIG_INVALID') throw error;
|
|
64
|
+
throw schedulingError('IRIS_SCHEDULER_CONFIG_IO', '无法读取 scheduler-config;请检查路径、权限和文件状态,不会退回无调度模式');
|
|
65
|
+
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
66
|
+
}
|
package/lib/tasks.js
CHANGED
|
@@ -718,7 +718,7 @@ export async function submitGuard(task, fn) {
|
|
|
718
718
|
/**
|
|
719
719
|
* 重启恢复:接管仍在运行的远程任务。DSH 重启期间百炼任务在服务端继续跑,
|
|
720
720
|
* 结果 URL 存活 24h,重启后重新盯守即可落袋。
|
|
721
|
-
* @param {(task) => deps|null} depsFor 返回盯守依赖;null
|
|
721
|
+
* @param {(task) => deps|null} depsFor 返回盯守依赖;null 仅暂停接管(如连接暂不可用),不证明远端失败
|
|
722
722
|
* @returns {string[]} 成功接管的任务 id
|
|
723
723
|
*/
|
|
724
724
|
export function resumePending(depsFor) {
|
|
@@ -737,9 +737,10 @@ export function resumePending(depsFor) {
|
|
|
737
737
|
}
|
|
738
738
|
const deps = depsFor(t);
|
|
739
739
|
if (!deps) {
|
|
740
|
-
update(t.id, {
|
|
740
|
+
update(t.id, { error: '无法恢复盯守:对应供应商不可用;远端任务状态未改变,可在连接恢复后再次接管' });
|
|
741
741
|
continue;
|
|
742
742
|
}
|
|
743
|
+
if (t.error) update(t.id, { error: undefined });
|
|
743
744
|
watch(t, deps);
|
|
744
745
|
resumed.push(t.id);
|
|
745
746
|
continue;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { normalizeGenerationInput } from './generation-input.js';
|
|
2
|
+
import { parseModelRef } from './models.js';
|
|
3
|
+
|
|
4
|
+
export const VIDEO_RECIPE = 'video-generate/v0';
|
|
5
|
+
export const MAX_VIDEO_BYTES = 32 * 1024 * 1024;
|
|
6
|
+
export const VIDEO_TYPES = Object.freeze({ 'video/mp4': 'mp4', 'video/webm': 'webm' });
|
|
7
|
+
export function normalizeVideoFlowInput(input) {
|
|
8
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)
|
|
9
|
+
|| Object.keys(input).some(key => !['model_ref', 'prompt', 'size', 'duration'].includes(key))
|
|
10
|
+
|| !parseModelRef(input.model_ref) || /s2v/i.test(parseModelRef(input.model_ref).modelId)
|
|
11
|
+
|| input.duration !== undefined && (typeof input.duration !== 'number' || !Number.isFinite(input.duration))) {
|
|
12
|
+
throw Object.assign(new Error('文生视频仅接受明确模型、prompt 和可选 size/duration;不接受首帧、S2V 或路径'), { code: 'IRIS_VIDEO_FLOW_INPUT_INVALID' });
|
|
13
|
+
}
|
|
14
|
+
const value = normalizeGenerationInput('video', input);
|
|
15
|
+
return { model_ref: value.modelRef, ...value.providerInput };
|
|
16
|
+
}
|
package/lib/video-input.js
CHANGED
|
@@ -7,19 +7,26 @@ import { parseModelRef } from './models.js';
|
|
|
7
7
|
import { prepareProviderInput } from './provider-adapter.js';
|
|
8
8
|
import { ProviderContractError } from './provider-contract.js';
|
|
9
9
|
|
|
10
|
+
/** Recognized input families constrain routing; opaque compatible IDs keep their existing t2v/i2v support. */
|
|
11
|
+
export function videoModelSupportsMode(modelId, mode) {
|
|
12
|
+
const family = /(?:^|[-_.])(t2v|i2v|s2v|r2v)(?=$|[-_.])/i.exec(modelId)?.[1].toLowerCase();
|
|
13
|
+
if (family) return family === mode;
|
|
14
|
+
return mode === 't2v' || mode === 'i2v';
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export function videoTaskCandidates(candidates, normalized) {
|
|
11
18
|
const s2v = normalized.videoMode === 's2v';
|
|
12
|
-
const compatible = candidates.filter(candidate =>
|
|
19
|
+
const compatible = candidates.filter(candidate => videoModelSupportsMode(parseModelRef(candidate.model)?.modelId || '', normalized.videoMode));
|
|
13
20
|
if (!compatible.length) throw new GenerationInputError(s2v
|
|
14
|
-
? 'audio_path 只能配合 s2v 数字人模型' : 's2v 模型需要首帧和音频输入');
|
|
21
|
+
? 'audio_path 只能配合 s2v 数字人模型' : '没有与 ' + normalized.videoMode + ' 输入匹配的视频模型;s2v 模型需要首帧和音频输入');
|
|
15
22
|
return compatible.map(candidate => ({ ...candidate,
|
|
16
|
-
...(normalized.firstFramePath ? { async prepareInput({ signal }) {
|
|
23
|
+
...(normalized.firstFramePath ? { async prepareInput({ signal, taskId, attemptId, networkBudget, recordNetworkRoute }) {
|
|
17
24
|
if (signal.aborted) throw new ProviderContractError('视频输入准备已取消', { stage: 'prepare', category: 'aborted', acceptance: 'not_accepted' });
|
|
18
25
|
if (s2v) {
|
|
19
26
|
const model = parseModelRef(candidate.model).modelId;
|
|
20
|
-
const image = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.firstFramePath, signal });
|
|
27
|
+
const image = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.firstFramePath, signal }, { taskId, attemptId, networkBudget, recordNetworkRoute });
|
|
21
28
|
if (signal.aborted) throw new ProviderContractError('视频输入准备已取消', { stage: 'upload', category: 'aborted', acceptance: 'not_accepted' });
|
|
22
|
-
const audio = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.audioPath, signal });
|
|
29
|
+
const audio = await prepareProviderInput(candidate.adapter, { model, filePath: normalized.audioPath, signal }, { taskId, attemptId, networkBudget, recordNetworkRoute });
|
|
23
30
|
return { ...normalized.providerInput, imgDataUrl: image.url, audioUrl: audio.url };
|
|
24
31
|
}
|
|
25
32
|
const mime = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.webp': 'image/webp', '.gif': 'image/gif' }[path.extname(normalized.firstFramePath).toLowerCase()];
|
|
@@ -12,6 +12,7 @@ import { modelAvailabilityBinding, modelUnavailable } from './model-directory.js
|
|
|
12
12
|
import { completeVision, VISION_BUDGET } from './vision-core.js';
|
|
13
13
|
import { open } from 'node:fs/promises';
|
|
14
14
|
import { DEFAULT_VISION_INPUT, visionInputLimits } from './vision-image-input.js';
|
|
15
|
+
import { providerNetworkRequirement, networkPolicyError } from './network-policy.js';
|
|
15
16
|
|
|
16
17
|
const features = Object.freeze({ system: 'unknown', temperature: 'unknown', maxOutputTokens: 'unknown', reasoning: 'unknown' });
|
|
17
18
|
|
|
@@ -50,6 +51,7 @@ function deferredDshPort(host, { prepareImages, orientImages, prepareImageReques
|
|
|
50
51
|
/** 配置只绑定一个自持模型;CLI 不附加宿主候选。 */
|
|
51
52
|
export function createConfiguredVisionModelPort(provider, model, { allowRateLimited = false, onRateLimit, onModelAvailability, prepareImages = false,
|
|
52
53
|
orientImages = false, prepareImageRequest } = {}) {
|
|
54
|
+
if (providerNetworkRequirement(provider)) throw networkPolicyError('IRIS_NETWORK_CAPABILITY_UNSUPPORTED', '独立视觉尚未接入此网络目录;已停止调用,不会默认直连。请保留原视觉配置');
|
|
53
55
|
const parsed = parseModelRef(resolveModelRef(provider, 'vision', model));
|
|
54
56
|
const id = 'iris-provider:' + encodeURIComponent(provider.id);
|
|
55
57
|
if (!parsed) return unavailablePort(id);
|