omnius 1.0.636 → 1.0.638
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/dist/index.js +656 -357
- package/docs/DISCOVERY.json +4 -4
- package/docs/DISCOVERY.md +2 -2
- package/docs/rest/endpoints/voice-vision.md +29 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5824,6 +5824,17 @@ function managedEnv3(extra = {}) {
|
|
|
5824
5824
|
function sha256File4(path16) {
|
|
5825
5825
|
return createHash6("sha256").update(readFileSync10(path16)).digest("hex");
|
|
5826
5826
|
}
|
|
5827
|
+
function speakerSupportManifest() {
|
|
5828
|
+
return SPEAKER_SUPPORT_WHEELS.map((wheel) => ({
|
|
5829
|
+
distribution: wheel.distribution,
|
|
5830
|
+
version: wheel.version,
|
|
5831
|
+
digest: `sha256:${wheel.digest}`
|
|
5832
|
+
}));
|
|
5833
|
+
}
|
|
5834
|
+
function hasExpectedSpeakerSupportPackages(packages) {
|
|
5835
|
+
const expected = speakerSupportManifest();
|
|
5836
|
+
return Array.isArray(packages) && packages.length === expected.length && expected.every((item) => packages.some((candidate) => candidate.distribution === item.distribution && candidate.version === item.version && candidate.digest === item.digest));
|
|
5837
|
+
}
|
|
5827
5838
|
function usesSystemSitePackages() {
|
|
5828
5839
|
try {
|
|
5829
5840
|
return /^include-system-site-packages\s*=\s*true\s*$/im.test(readFileSync10(join11(venvDir2(), "pyvenv.cfg"), "utf8"));
|
|
@@ -5834,7 +5845,7 @@ function usesSystemSitePackages() {
|
|
|
5834
5845
|
function loadManifest3() {
|
|
5835
5846
|
try {
|
|
5836
5847
|
const manifest = JSON.parse(readFileSync10(manifestPath3(), "utf8"));
|
|
5837
|
-
if (manifest.runtimeVersion !== RUNTIME_VERSION3 || manifest.model !== MODEL_NAME2 || manifest.modelRevision !== MODEL_REVISION2 || manifest.modelDigest !== `sha256:${MODEL_SHA2562}` || manifest.backend !== "onnxruntime-cpu" || manifest.onnxRuntimeVersion !== ONNXRUNTIME_VERSION || manifest.torchaudioVersion !== TORCHAUDIO_VERSION || !manifest.torchaudioWheel || !manifest.torchVersion || !manifest.torchSitePackages || !existsSync11(modelPath2()) || !existsSync11(managedPython())) {
|
|
5848
|
+
if (manifest.runtimeVersion !== RUNTIME_VERSION3 || manifest.model !== MODEL_NAME2 || manifest.modelRevision !== MODEL_REVISION2 || manifest.modelDigest !== `sha256:${MODEL_SHA2562}` || manifest.backend !== "onnxruntime-cpu" || manifest.onnxRuntimeVersion !== ONNXRUNTIME_VERSION || manifest.torchaudioVersion !== TORCHAUDIO_VERSION || !hasExpectedSpeakerSupportPackages(manifest.supportPackages) || !manifest.torchaudioWheel || !manifest.torchVersion || !manifest.torchSitePackages || !existsSync11(modelPath2()) || !existsSync11(managedPython())) {
|
|
5838
5849
|
return null;
|
|
5839
5850
|
}
|
|
5840
5851
|
return sha256File4(modelPath2()) === MODEL_SHA2562 ? manifest : null;
|
|
@@ -5864,6 +5875,25 @@ function requiredJetson2() {
|
|
|
5864
5875
|
function bootstrapPython() {
|
|
5865
5876
|
return process.env["OMNIUS_AUDIO_PYTHON"]?.trim() || "python3";
|
|
5866
5877
|
}
|
|
5878
|
+
function processDiagnostic(error) {
|
|
5879
|
+
if (error instanceof AsyncProcessError) {
|
|
5880
|
+
const detail = `${error.stderr.toString("utf8")}
|
|
5881
|
+
${error.stdout.toString("utf8")}`.trim();
|
|
5882
|
+
if (detail)
|
|
5883
|
+
return detail.slice(-4e3);
|
|
5884
|
+
}
|
|
5885
|
+
return (error instanceof Error ? error.message : String(error)).slice(-4e3);
|
|
5886
|
+
}
|
|
5887
|
+
function describeSpeakerRuntimeProbeError(detail) {
|
|
5888
|
+
const match = detail.match(/ModuleNotFoundError:\s*No module named ['"]([^'"]+)['"]/i);
|
|
5889
|
+
if (match?.[1]) {
|
|
5890
|
+
return `Managed speaker runtime is missing Python module '${match[1]}'. It is intentionally isolated from project/user site packages.`;
|
|
5891
|
+
}
|
|
5892
|
+
return detail.slice(-4e3);
|
|
5893
|
+
}
|
|
5894
|
+
function runtimeProbeError(error) {
|
|
5895
|
+
return describeSpeakerRuntimeProbeError(processDiagnostic(error));
|
|
5896
|
+
}
|
|
5867
5897
|
async function probeRuntime(python2) {
|
|
5868
5898
|
if (!python2 || python2.includes("/") && !existsSync11(python2)) {
|
|
5869
5899
|
return { available: false, providers: [] };
|
|
@@ -5903,8 +5933,8 @@ async function probeRuntime(python2) {
|
|
|
5903
5933
|
torchaudioVersion: parsed.torchaudio,
|
|
5904
5934
|
providers
|
|
5905
5935
|
};
|
|
5906
|
-
} catch {
|
|
5907
|
-
return { available: false, providers: [] };
|
|
5936
|
+
} catch (error) {
|
|
5937
|
+
return { available: false, providers: [], error: runtimeProbeError(error) };
|
|
5908
5938
|
}
|
|
5909
5939
|
}
|
|
5910
5940
|
function isExpectedJetPackTorchVersion(version5) {
|
|
@@ -5915,26 +5945,24 @@ async function inspectBootstrapPython(python2) {
|
|
|
5915
5945
|
throw new Error("OMNIUS_AUDIO_PYTHON does not name a usable JetPack Python interpreter");
|
|
5916
5946
|
}
|
|
5917
5947
|
const script = [
|
|
5918
|
-
"import json,os,sys,sysconfig
|
|
5948
|
+
"import json,os,sys,sysconfig",
|
|
5919
5949
|
"assert sys.version_info[:2] == (3, 10), 'CPython 3.10 is required by the pinned ONNX Runtime wheel'",
|
|
5920
5950
|
"site = sysconfig.get_paths()['purelib']",
|
|
5921
5951
|
"assert os.path.isdir(site), 'JetPack Python has no usable site-packages directory'",
|
|
5922
|
-
"print(json.dumps({'python':sys.version.split()[0], '
|
|
5952
|
+
"print(json.dumps({'python':sys.version.split()[0], 'site':site}))"
|
|
5923
5953
|
].join("\n");
|
|
5924
5954
|
try {
|
|
5925
5955
|
const output2 = await execFileText2(python2, ["-c", script], { timeout: 3e4, env: managedEnv3() });
|
|
5926
5956
|
const parsed = JSON.parse(output2.trim().split(/\r?\n/).pop() || "{}");
|
|
5927
|
-
if (!parsed.python || !parsed.
|
|
5928
|
-
throw new Error(
|
|
5957
|
+
if (!parsed.python || !parsed.site || !isAbsolute3(parsed.site) || !existsSync11(parsed.site)) {
|
|
5958
|
+
throw new Error("OMNIUS_AUDIO_PYTHON did not expose a usable CPython 3.10 site-packages directory");
|
|
5929
5959
|
}
|
|
5930
5960
|
return {
|
|
5931
5961
|
pythonVersion: parsed.python,
|
|
5932
|
-
torchVersion: parsed.torch,
|
|
5933
|
-
torchCudaVersion: parsed.torch_cuda ?? null,
|
|
5934
5962
|
torchSitePackages: resolvePath(parsed.site)
|
|
5935
5963
|
};
|
|
5936
5964
|
} catch (error) {
|
|
5937
|
-
throw new Error(`
|
|
5965
|
+
throw new Error(`OMNIUS_AUDIO_PYTHON bootstrap inspection failed: ${processDiagnostic(error)}`);
|
|
5938
5966
|
}
|
|
5939
5967
|
}
|
|
5940
5968
|
async function managedSitePackages(python2) {
|
|
@@ -5999,6 +6027,38 @@ async function materializeTorchaudioWheel(wheel) {
|
|
|
5999
6027
|
}
|
|
6000
6028
|
return source;
|
|
6001
6029
|
}
|
|
6030
|
+
async function installPinnedSpeakerSupportDependencies(python2) {
|
|
6031
|
+
for (const wheel of SPEAKER_SUPPORT_WHEELS) {
|
|
6032
|
+
const filename = basename(new URL(wheel.source).pathname);
|
|
6033
|
+
if (!filename.endsWith(".whl")) {
|
|
6034
|
+
throw new Error(`Pinned speaker support wheel for ${wheel.distribution} has an invalid filename`);
|
|
6035
|
+
}
|
|
6036
|
+
const localWheel = join11(speakerEmbeddingRuntimeDir(), "artifacts", wheel.digest, filename);
|
|
6037
|
+
try {
|
|
6038
|
+
await downloadChecked2(wheel.source, localWheel, wheel.digest);
|
|
6039
|
+
await execFileText2(python2, [
|
|
6040
|
+
"-m",
|
|
6041
|
+
"pip",
|
|
6042
|
+
"install",
|
|
6043
|
+
"--disable-pip-version-check",
|
|
6044
|
+
"--no-deps",
|
|
6045
|
+
"--no-index",
|
|
6046
|
+
"--force-reinstall",
|
|
6047
|
+
localWheel
|
|
6048
|
+
], { timeout: SETUP_TIMEOUT_MS3, env: managedEnv3() });
|
|
6049
|
+
await execFileText2(python2, [
|
|
6050
|
+
"-c",
|
|
6051
|
+
[
|
|
6052
|
+
"import importlib.metadata",
|
|
6053
|
+
`import ${wheel.importName}`,
|
|
6054
|
+
`assert importlib.metadata.version('${wheel.distribution}') == '${wheel.version}'`
|
|
6055
|
+
].join("\n")
|
|
6056
|
+
], { timeout: 3e4, env: managedEnv3() });
|
|
6057
|
+
} catch (error) {
|
|
6058
|
+
throw new Error(`Managed speaker venv could not install required support module '${wheel.importName}' (${wheel.distribution}==${wheel.version}): ${processDiagnostic(error)}`);
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
6002
6062
|
async function installPinnedTorchaudio(python2, wheel) {
|
|
6003
6063
|
const localWheel = await materializeTorchaudioWheel(wheel);
|
|
6004
6064
|
await execFileText2(python2, [
|
|
@@ -6088,6 +6148,7 @@ async function getSpeakerEmbeddingReadiness() {
|
|
|
6088
6148
|
torch_version: runtime2.torchVersion,
|
|
6089
6149
|
torch_cuda_version: runtime2.torchCudaVersion,
|
|
6090
6150
|
torchaudio_version: runtime2.torchaudioVersion,
|
|
6151
|
+
support_packages: manifest?.supportPackages,
|
|
6091
6152
|
...manifest ? {
|
|
6092
6153
|
torchaudio_wheel: {
|
|
6093
6154
|
source: manifest.torchaudioWheel.source,
|
|
@@ -6097,7 +6158,8 @@ async function getSpeakerEmbeddingReadiness() {
|
|
|
6097
6158
|
torch_site_packages: manifest.torchSitePackages
|
|
6098
6159
|
} : {},
|
|
6099
6160
|
providers: runtime2.providers,
|
|
6100
|
-
imports_verified: importsVerified
|
|
6161
|
+
imports_verified: importsVerified,
|
|
6162
|
+
...runtime2.error ? { probe_error: runtime2.error } : {}
|
|
6101
6163
|
},
|
|
6102
6164
|
worker: {
|
|
6103
6165
|
ready: Boolean(state3.ready && state3.worker?.exitCode === null && state3.worker.signalCode === null),
|
|
@@ -6120,12 +6182,13 @@ async function ensureSpeakerEmbeddingSetup() {
|
|
|
6120
6182
|
const bootstrapRuntime = await inspectBootstrapPython(bootstrap2);
|
|
6121
6183
|
await createManagedVenv(bootstrap2, bootstrapRuntime.torchSitePackages);
|
|
6122
6184
|
const python2 = managedPython();
|
|
6185
|
+
await installPinnedSpeakerSupportDependencies(python2);
|
|
6123
6186
|
const torchaudioWheel = configuredTorchaudioWheel();
|
|
6124
6187
|
await installPinnedTorchaudio(python2, torchaudioWheel);
|
|
6125
6188
|
await installPinnedOnnxRuntime(python2);
|
|
6126
6189
|
const runtime2 = await probeRuntime(python2);
|
|
6127
6190
|
if (!runtime2.available || runtime2.torchaudioVersion !== TORCHAUDIO_VERSION || !runtime2.torchVersion || !isExpectedJetPackTorchVersion(runtime2.torchVersion) || !String(runtime2.torchCudaVersion ?? "").startsWith(JETPACK_CUDA_VERSION) || !runtime2.torchPath || !resolvePath(runtime2.torchPath).startsWith(bootstrapRuntime.torchSitePackages) || !runtime2.providers.includes("CPUExecutionProvider")) {
|
|
6128
|
-
throw new Error(
|
|
6191
|
+
throw new Error(`Managed Torchaudio/Torch ABI validation failed after speaker setup${runtime2.error ? `: ${runtime2.error}` : ""}. The exact CPU kaldi.fbank probe must use the operator-selected JetPack Torch 2.2/CUDA 12.2 and managed torchaudio 2.2.0.`);
|
|
6129
6192
|
}
|
|
6130
6193
|
await downloadChecked2(MODEL_URL2, modelPath2(), MODEL_SHA2562);
|
|
6131
6194
|
const manifest = {
|
|
@@ -6136,6 +6199,7 @@ async function ensureSpeakerEmbeddingSetup() {
|
|
|
6136
6199
|
backend: "onnxruntime-cpu",
|
|
6137
6200
|
onnxRuntimeVersion: ONNXRUNTIME_VERSION,
|
|
6138
6201
|
torchaudioVersion: runtime2.torchaudioVersion,
|
|
6202
|
+
supportPackages: speakerSupportManifest(),
|
|
6139
6203
|
torchaudioWheel,
|
|
6140
6204
|
pythonVersion: runtime2.pythonVersion ?? bootstrapRuntime.pythonVersion,
|
|
6141
6205
|
torchVersion: runtime2.torchVersion,
|
|
@@ -6376,7 +6440,7 @@ async function bootstrapSpeakerEmbeddingRuntime() {
|
|
|
6376
6440
|
await ensureSpeakerEmbeddingSetup();
|
|
6377
6441
|
return activateSpeakerEmbedding();
|
|
6378
6442
|
}
|
|
6379
|
-
var RUNTIME_VERSION3, MODEL_NAME2, MODEL_REVISION2, MODEL_FILE, MODEL_URL2, MODEL_SHA2562, ONNXRUNTIME_VERSION, ONNXRUNTIME_WHEEL, ONNXRUNTIME_WHEEL_SHA256, TORCHAUDIO_VERSION, TORCHAUDIO_WHEEL, TORCHAUDIO_WHEEL_SHA256, JETPACK_TORCH_ABI, JETPACK_CUDA_VERSION, SETUP_TIMEOUT_MS3, START_TIMEOUT_MS3, EMBED_TIMEOUT_MS2, WESPEAKER_CAMPP_ONNX_SHA256, WESPEAKER_CAMPP_MODEL_REVISION, WESPEAKER_CAMPP_ONNXRUNTIME_VERSION, WESPEAKER_CAMPP_EMBEDDING_SET_SLUG, state3;
|
|
6443
|
+
var RUNTIME_VERSION3, MODEL_NAME2, MODEL_REVISION2, MODEL_FILE, MODEL_URL2, MODEL_SHA2562, ONNXRUNTIME_VERSION, ONNXRUNTIME_WHEEL, ONNXRUNTIME_WHEEL_SHA256, TORCHAUDIO_VERSION, TORCHAUDIO_WHEEL, TORCHAUDIO_WHEEL_SHA256, TYPING_EXTENSIONS_VERSION, TYPING_EXTENSIONS_WHEEL, TYPING_EXTENSIONS_WHEEL_SHA256, JETPACK_TORCH_ABI, JETPACK_CUDA_VERSION, SETUP_TIMEOUT_MS3, START_TIMEOUT_MS3, EMBED_TIMEOUT_MS2, WESPEAKER_CAMPP_ONNX_SHA256, WESPEAKER_CAMPP_MODEL_REVISION, WESPEAKER_CAMPP_ONNXRUNTIME_VERSION, WESPEAKER_CAMPP_EMBEDDING_SET_SLUG, SPEAKER_SUPPORT_WHEELS, state3;
|
|
6380
6444
|
var init_speaker_embedding_runtime = __esm({
|
|
6381
6445
|
"packages/execution/dist/speaker-embedding-runtime.js"() {
|
|
6382
6446
|
"use strict";
|
|
@@ -6384,7 +6448,7 @@ var init_speaker_embedding_runtime = __esm({
|
|
|
6384
6448
|
init_process_async();
|
|
6385
6449
|
init_model_store();
|
|
6386
6450
|
init_venv_paths();
|
|
6387
|
-
RUNTIME_VERSION3 =
|
|
6451
|
+
RUNTIME_VERSION3 = 3;
|
|
6388
6452
|
MODEL_NAME2 = "wespeaker-voxceleb-campplus";
|
|
6389
6453
|
MODEL_REVISION2 = "acf623ad8ca746e50baa432255cf8fc57c669c45";
|
|
6390
6454
|
MODEL_FILE = "voxceleb_CAM++.onnx";
|
|
@@ -6396,6 +6460,9 @@ var init_speaker_embedding_runtime = __esm({
|
|
|
6396
6460
|
TORCHAUDIO_VERSION = "2.2.0";
|
|
6397
6461
|
TORCHAUDIO_WHEEL = "https://files.pythonhosted.org/packages/45/a5/74d8a03fdf47cf89e9a2f6c58a65ffe4b392e8cfa503f148baec43377f24/torchaudio-2.2.0-cp310-cp310-manylinux2014_aarch64.whl";
|
|
6398
6462
|
TORCHAUDIO_WHEEL_SHA256 = "d4ea094b8721a361982db062ee993f2a6f71dfe16f62a84f8900b2364f33a2e4";
|
|
6463
|
+
TYPING_EXTENSIONS_VERSION = "4.10.0";
|
|
6464
|
+
TYPING_EXTENSIONS_WHEEL = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl";
|
|
6465
|
+
TYPING_EXTENSIONS_WHEEL_SHA256 = "69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475";
|
|
6399
6466
|
JETPACK_TORCH_ABI = "2.2";
|
|
6400
6467
|
JETPACK_CUDA_VERSION = "12.2";
|
|
6401
6468
|
SETUP_TIMEOUT_MS3 = 10 * 6e4;
|
|
@@ -6405,6 +6472,15 @@ var init_speaker_embedding_runtime = __esm({
|
|
|
6405
6472
|
WESPEAKER_CAMPP_MODEL_REVISION = MODEL_REVISION2;
|
|
6406
6473
|
WESPEAKER_CAMPP_ONNXRUNTIME_VERSION = ONNXRUNTIME_VERSION;
|
|
6407
6474
|
WESPEAKER_CAMPP_EMBEDDING_SET_SLUG = "speaker-identity-wespeaker-voxceleb-campplus-acf623ad8ca746e50baa432255cf8fc57c669c45-fbank80-cmn-fullclip-l2";
|
|
6475
|
+
SPEAKER_SUPPORT_WHEELS = [
|
|
6476
|
+
{
|
|
6477
|
+
distribution: "typing_extensions",
|
|
6478
|
+
importName: "typing_extensions",
|
|
6479
|
+
version: TYPING_EXTENSIONS_VERSION,
|
|
6480
|
+
source: TYPING_EXTENSIONS_WHEEL,
|
|
6481
|
+
digest: TYPING_EXTENSIONS_WHEEL_SHA256
|
|
6482
|
+
}
|
|
6483
|
+
];
|
|
6408
6484
|
state3 = {
|
|
6409
6485
|
setup: null,
|
|
6410
6486
|
start: null,
|
|
@@ -688266,9 +688342,12 @@ __export(listen_exports, {
|
|
|
688266
688342
|
getListenEngine: () => getListenEngine,
|
|
688267
688343
|
getListenLiveState: () => getListenLiveState,
|
|
688268
688344
|
isAudioPath: () => isAudioPath,
|
|
688345
|
+
isEggReSpeakerPulseSource: () => isEggReSpeakerPulseSource,
|
|
688346
|
+
isKnownVirtualPulseRemap: () => isKnownVirtualPulseRemap,
|
|
688269
688347
|
isTranscribablePath: () => isTranscribablePath,
|
|
688270
688348
|
isVideoPath: () => isVideoPath,
|
|
688271
688349
|
resolveAsrConsensusModel: () => resolveAsrConsensusModel,
|
|
688350
|
+
selectPreferredPulseMicSource: () => selectPreferredPulseMicSource,
|
|
688272
688351
|
transcribeFileViaWhisper: () => transcribeFileViaWhisper,
|
|
688273
688352
|
waitForTranscribeCli: () => waitForTranscribeCli
|
|
688274
688353
|
});
|
|
@@ -688351,10 +688430,30 @@ function renderMicSpectrum(samples) {
|
|
|
688351
688430
|
}
|
|
688352
688431
|
return bands.join("");
|
|
688353
688432
|
}
|
|
688433
|
+
function isEggReSpeakerPulseSource(source) {
|
|
688434
|
+
return source.channels >= 6 && /(?:^|:)alsa_input\.usb-SEEED_ReSpeaker_4_Mic_Array__UAC1\.0_-00\.multichannel-input$/i.test(source.name);
|
|
688435
|
+
}
|
|
688436
|
+
function isKnownVirtualPulseRemap(sourceName) {
|
|
688437
|
+
const name10 = String(sourceName ?? "").replace(/^pulse:/i, "");
|
|
688438
|
+
return /^nx_remapped_out(?:[._-].*)?$/i.test(name10);
|
|
688439
|
+
}
|
|
688440
|
+
function selectPreferredPulseMicSource(sources) {
|
|
688441
|
+
const candidates = sources.filter((source) => !/\.monitor$/i.test(source.name));
|
|
688442
|
+
const rank = (source) => {
|
|
688443
|
+
let score = 0;
|
|
688444
|
+
if (isEggReSpeakerPulseSource(source)) score -= 1e3;
|
|
688445
|
+
if (isKnownVirtualPulseRemap(source.name)) score += 100;
|
|
688446
|
+
if (source.state === "RUNNING") score -= 20;
|
|
688447
|
+
if (source.state === "SUSPENDED") score += 5;
|
|
688448
|
+
if (/usb|mic|array|respeaker|seeed/i.test(source.name)) score -= 10;
|
|
688449
|
+
return score;
|
|
688450
|
+
};
|
|
688451
|
+
return [...candidates].sort((a2, b) => rank(a2) - rank(b))[0];
|
|
688452
|
+
}
|
|
688354
688453
|
async function resolveMicSource() {
|
|
688355
688454
|
const configured = String(process.env["OMNIUS_MIC_DEVICE"] ?? "").trim();
|
|
688356
688455
|
if (configured.startsWith("hw:") || configured.startsWith("plughw:")) {
|
|
688357
|
-
return { alsaDevice: configured, label: configured };
|
|
688456
|
+
return { alsaDevice: configured, sourceKind: "configured-alsa", label: configured };
|
|
688358
688457
|
}
|
|
688359
688458
|
const configuredPulse = configured.startsWith("pulse:") ? configured.slice("pulse:".length) : configured || void 0;
|
|
688360
688459
|
let sources = [];
|
|
@@ -688380,21 +688479,19 @@ async function resolveMicSource() {
|
|
|
688380
688479
|
return {
|
|
688381
688480
|
pulseSource: configuredPulse,
|
|
688382
688481
|
channels: match?.channels,
|
|
688482
|
+
sourceKind: "configured-pulse",
|
|
688383
688483
|
label: `pulse:${configuredPulse}`
|
|
688384
688484
|
};
|
|
688385
688485
|
}
|
|
688386
|
-
const
|
|
688387
|
-
if (
|
|
688388
|
-
const
|
|
688389
|
-
|
|
688390
|
-
|
|
688391
|
-
|
|
688392
|
-
|
|
688393
|
-
|
|
688486
|
+
const best = selectPreferredPulseMicSource(sources);
|
|
688487
|
+
if (!best) return null;
|
|
688488
|
+
const isReSpeaker = isEggReSpeakerPulseSource(best);
|
|
688489
|
+
return {
|
|
688490
|
+
pulseSource: best.name,
|
|
688491
|
+
channels: best.channels,
|
|
688492
|
+
sourceKind: isReSpeaker ? "respeaker-usb" : "pulse-auto",
|
|
688493
|
+
label: isReSpeaker ? `pulse:${best.name} (ReSpeaker physical 6ch; channel 0)` : `pulse:${best.name}`
|
|
688394
688494
|
};
|
|
688395
|
-
candidates.sort((a2, b) => rank(a2) - rank(b));
|
|
688396
|
-
const best = candidates[0];
|
|
688397
|
-
return { pulseSource: best.name, channels: best.channels, label: `pulse:${best.name}` };
|
|
688398
688495
|
}
|
|
688399
688496
|
async function ensureSourceCaptureVolume(source) {
|
|
688400
688497
|
try {
|
|
@@ -688431,7 +688528,10 @@ async function findMicCaptureCommand() {
|
|
|
688431
688528
|
return {
|
|
688432
688529
|
cmd: "arecord",
|
|
688433
688530
|
args: ["-D", resolved.alsaDevice, "-f", "S16_LE", "-r", "16000", "-c", "1", "-t", "raw", "-q", "-"],
|
|
688434
|
-
device: resolved.alsaDevice
|
|
688531
|
+
device: resolved.alsaDevice,
|
|
688532
|
+
sourceKind: resolved.sourceKind,
|
|
688533
|
+
sourceChannels: null,
|
|
688534
|
+
selectedChannel: null
|
|
688435
688535
|
};
|
|
688436
688536
|
}
|
|
688437
688537
|
if (resolved?.pulseSource && await commandExists2("ffmpeg")) {
|
|
@@ -688455,14 +688555,20 @@ async function findMicCaptureCommand() {
|
|
|
688455
688555
|
"quiet",
|
|
688456
688556
|
"pipe:1"
|
|
688457
688557
|
],
|
|
688458
|
-
device: resolved.label
|
|
688558
|
+
device: resolved.label,
|
|
688559
|
+
sourceKind: resolved.sourceKind,
|
|
688560
|
+
sourceChannels: resolved.channels ?? null,
|
|
688561
|
+
selectedChannel: (resolved.channels ?? 1) > 2 ? 0 : null
|
|
688459
688562
|
};
|
|
688460
688563
|
}
|
|
688461
688564
|
if (await commandExists2("arecord")) {
|
|
688462
688565
|
return {
|
|
688463
688566
|
cmd: "arecord",
|
|
688464
688567
|
args: ["-f", "S16_LE", "-r", "16000", "-c", "1", "-t", "raw", "-q", "-"],
|
|
688465
|
-
device: "default (arecord)"
|
|
688568
|
+
device: "default (arecord)",
|
|
688569
|
+
sourceKind: "fallback",
|
|
688570
|
+
sourceChannels: null,
|
|
688571
|
+
selectedChannel: null
|
|
688466
688572
|
};
|
|
688467
688573
|
}
|
|
688468
688574
|
}
|
|
@@ -688484,7 +688590,10 @@ async function findMicCaptureCommand() {
|
|
|
688484
688590
|
"signed-integer",
|
|
688485
688591
|
"-"
|
|
688486
688592
|
],
|
|
688487
|
-
device: "default (sox)"
|
|
688593
|
+
device: "default (sox)",
|
|
688594
|
+
sourceKind: "fallback",
|
|
688595
|
+
sourceChannels: null,
|
|
688596
|
+
selectedChannel: null
|
|
688488
688597
|
};
|
|
688489
688598
|
}
|
|
688490
688599
|
}
|
|
@@ -688507,7 +688616,10 @@ async function findMicCaptureCommand() {
|
|
|
688507
688616
|
"quiet",
|
|
688508
688617
|
"pipe:1"
|
|
688509
688618
|
],
|
|
688510
|
-
device: "pulse:default"
|
|
688619
|
+
device: "pulse:default",
|
|
688620
|
+
sourceKind: "fallback",
|
|
688621
|
+
sourceChannels: null,
|
|
688622
|
+
selectedChannel: null
|
|
688511
688623
|
};
|
|
688512
688624
|
} else if (platform13 === "darwin") {
|
|
688513
688625
|
return {
|
|
@@ -688527,7 +688639,10 @@ async function findMicCaptureCommand() {
|
|
|
688527
688639
|
"quiet",
|
|
688528
688640
|
"pipe:1"
|
|
688529
688641
|
],
|
|
688530
|
-
device: "avfoundation:0"
|
|
688642
|
+
device: "avfoundation:0",
|
|
688643
|
+
sourceKind: "fallback",
|
|
688644
|
+
sourceChannels: null,
|
|
688645
|
+
selectedChannel: null
|
|
688531
688646
|
};
|
|
688532
688647
|
}
|
|
688533
688648
|
}
|
|
@@ -688851,6 +688966,9 @@ var init_listen = __esm({
|
|
|
688851
688966
|
backend: "",
|
|
688852
688967
|
model: "",
|
|
688853
688968
|
micDevice: "",
|
|
688969
|
+
micSourceKind: "unknown",
|
|
688970
|
+
micSourceChannels: null,
|
|
688971
|
+
micSelectedChannel: null,
|
|
688854
688972
|
micLevelDb: null,
|
|
688855
688973
|
noiseFloorDb: null,
|
|
688856
688974
|
speechActive: false,
|
|
@@ -689284,7 +689402,12 @@ ${text3}`.slice(-2e3);
|
|
|
689284
689402
|
stdio: ["pipe", "pipe", "pipe"],
|
|
689285
689403
|
env: { ...process.env }
|
|
689286
689404
|
});
|
|
689287
|
-
updateListenLiveState({
|
|
689405
|
+
updateListenLiveState({
|
|
689406
|
+
micDevice: micCmd.device,
|
|
689407
|
+
micSourceKind: micCmd.sourceKind,
|
|
689408
|
+
micSourceChannels: micCmd.sourceChannels,
|
|
689409
|
+
micSelectedChannel: micCmd.selectedChannel
|
|
689410
|
+
});
|
|
689288
689411
|
this.micProcess.stdout?.on("data", (chunk) => {
|
|
689289
689412
|
this.meterMicChunk(chunk);
|
|
689290
689413
|
if (this.active && !this.paused && this.liveTranscriber) {
|
|
@@ -692573,14 +692696,28 @@ function isAudioOutputMonitorDevice(device2) {
|
|
|
692573
692696
|
if (!device2) return false;
|
|
692574
692697
|
return isAudioOutputMonitorId(device2.id) || isAudioOutputMonitorId(device2.label) || isAudioOutputMonitorId(device2.detail);
|
|
692575
692698
|
}
|
|
692699
|
+
function isEggReSpeakerLiveInput(device2) {
|
|
692700
|
+
if (!device2?.id.startsWith("pulse:")) return false;
|
|
692701
|
+
return isEggReSpeakerPulseSource({
|
|
692702
|
+
name: device2.id.slice("pulse:".length),
|
|
692703
|
+
channels: liveDeviceChannels(device2) ?? 0
|
|
692704
|
+
});
|
|
692705
|
+
}
|
|
692706
|
+
function shouldReplaceStaleVirtualAudioSelection(configured, usable) {
|
|
692707
|
+
return Boolean(configured && isKnownVirtualPulseRemap(configured) && usable.some(isEggReSpeakerLiveInput));
|
|
692708
|
+
}
|
|
692576
692709
|
function preferredLiveAudioInputId(devices, configured) {
|
|
692577
692710
|
const usable = devices.filter((device2) => device2.id && !isAudioOutputMonitorDevice(device2));
|
|
692578
|
-
if (configured && usable.some((device2) => device2.id === configured)
|
|
692711
|
+
if (configured && usable.some((device2) => device2.id === configured) && !shouldReplaceStaleVirtualAudioSelection(configured, usable)) {
|
|
692712
|
+
return configured;
|
|
692713
|
+
}
|
|
692579
692714
|
return rankLiveAudioInputDevices(devices, configured)[0]?.id ?? firstDeviceId(devices);
|
|
692580
692715
|
}
|
|
692581
692716
|
function liveAudioDeviceRank(device2, configured) {
|
|
692582
692717
|
let rank = 0;
|
|
692583
692718
|
if (device2.id === configured) rank -= 40;
|
|
692719
|
+
if (isEggReSpeakerLiveInput(device2)) rank -= 1e3;
|
|
692720
|
+
if (isKnownVirtualPulseRemap(device2.id)) rank += 100;
|
|
692584
692721
|
if (isAudioOutputMonitorDevice(device2)) rank += 500;
|
|
692585
692722
|
if (device2.source === "pulse" || device2.source === "pipewire") rank -= 20;
|
|
692586
692723
|
if (device2.source === "alsa") rank -= 10;
|
|
@@ -693202,7 +693339,10 @@ var init_live_sensors = __esm({
|
|
|
693202
693339
|
};
|
|
693203
693340
|
if (!this.config.selectedCamera) this.config.selectedCamera = firstDeviceId(video);
|
|
693204
693341
|
this.config.cameraStreams = this.reconcileCameraStreams(video);
|
|
693205
|
-
|
|
693342
|
+
const preferredAudioInput = preferredLiveAudioInputId(inputs.devices, this.config.selectedAudioInput);
|
|
693343
|
+
if (!this.config.selectedAudioInput || shouldReplaceStaleVirtualAudioSelection(this.config.selectedAudioInput, inputs.devices)) {
|
|
693344
|
+
this.config.selectedAudioInput = preferredAudioInput;
|
|
693345
|
+
}
|
|
693206
693346
|
if (!this.config.selectedAudioOutput) this.config.selectedAudioOutput = firstDeviceId(outputs.devices);
|
|
693207
693347
|
this.persist();
|
|
693208
693348
|
return this.devices;
|
|
@@ -792531,6 +792671,10 @@ function getRuntimeStatus() {
|
|
|
792531
792671
|
asrBackend: asrEngineId === "vibevoice-transformers" ? "vibevoice-transformers" : listenState.backend,
|
|
792532
792672
|
asrPhase: asrEngineId === "vibevoice-transformers" ? vibePhase : listenState.phase,
|
|
792533
792673
|
asrReady: asrEngineId === "vibevoice-transformers" ? vibe.active : Boolean(_listenEngine?.isActive),
|
|
792674
|
+
micDevice: listenState.micDevice || null,
|
|
792675
|
+
micSourceKind: listenState.micSourceKind,
|
|
792676
|
+
micSourceChannels: listenState.micSourceChannels,
|
|
792677
|
+
micSelectedChannel: listenState.micSelectedChannel,
|
|
792534
792678
|
clientCount: _clients2.size,
|
|
792535
792679
|
loadedAt: _loadedAt,
|
|
792536
792680
|
lastError: _lastError
|
|
@@ -828308,7 +828452,7 @@ function getOpenApiSpec() {
|
|
|
828308
828452
|
"/v1/agents": { get: { summary: "List agent types", tags: ["Tools"], responses: { 200: { description: "Agent type registry" } } } },
|
|
828309
828453
|
"/v1/engines": { get: { summary: "List long-running engines", tags: ["Engines"], responses: { 200: { description: "Engine status + state files" } } } },
|
|
828310
828454
|
// ───── Voice / Audio surface (live) ─────
|
|
828311
|
-
"/v1/voice/state": { get: { summary: "Voice runtime status (engine
|
|
828455
|
+
"/v1/voice/state": { get: { summary: "Voice runtime status (engine/model state plus actual selected microphone source and channel provenance)", tags: ["Voice"], responses: { 200: { description: "Runtime status snapshot" } } } },
|
|
828312
828456
|
"/v1/voice/start": { post: { summary: "Enable and warm the daemon voice runtime; optional body {modelId|model|voice}", tags: ["Voice"], responses: { 200: { description: "Voice enabled and ready" }, 400: { description: "Unknown model" }, 409: { description: "Model disabled by configuration" }, 500: { description: "Warmup failed" } } } },
|
|
828313
828457
|
"/v1/voice/stop": { post: { summary: "Pause daemon voice input immediately (TTS models remain warm)", tags: ["Voice"], responses: { 200: { description: "Voice input paused" }, 500: { description: "Stop failed" } } } },
|
|
828314
828458
|
"/v1/voice/models": { get: { summary: "List TTS voice models with backend metadata and managed readiness", tags: ["Voice"], responses: { 200: { description: "Voice model list, active selection, and Voxtral pull/deploy state" } } } },
|
|
@@ -828436,7 +828580,7 @@ function getOpenApiSpec() {
|
|
|
828436
828580
|
post: {
|
|
828437
828581
|
summary: "Provision and activate one role-typed audio embedding runtime",
|
|
828438
828582
|
tags: ["Audio"],
|
|
828439
|
-
description: "Admin-only. Requires kind=acoustic|speaker|semantic in the query (canonical) or JSON body. acoustic reuses the pinned JetPack YAMNet/TensorRT setup; speaker installs the isolated CPU-only WeSpeaker CAM++ ONNX Runtime and pinned model, then installs only
|
|
828583
|
+
description: "Admin-only. Requires kind=acoustic|speaker|semantic in the query (canonical) or JSON body. acoustic reuses the pinned JetPack YAMNet/TensorRT setup; speaker installs the isolated CPU-only WeSpeaker CAM++ ONNX Runtime and pinned model, then installs only checksummed wheels into ~/.omnius/runtimes/audio/speaker/venv: CPython 3.10/aarch64 Torchaudio 2.2.0 and the self-contained typing_extensions support dependency, each with --no-deps/--no-index. It retains --system-site-packages and links the selected OMNIUS_AUDIO_PYTHON JetPack Torch 2.2/CUDA 12.2 provider without writing to it; setup proves the Torch/Torchaudio ABI and CPU kaldi.fbank before readiness. An operator may replace the Torchaudio wheel only by supplying both OMNIUS_SPEAKER_TORCHAUDIO_WHEEL and OMNIUS_SPEAKER_TORCHAUDIO_WHEEL_SHA256. semantic installs the isolated JetPack CUDA CLAP dependencies and pinned model. This is the only REST operation allowed to provision. It activates and warms only the requested role worker; no role is substituted for another.",
|
|
828440
828584
|
parameters: [{ name: "kind", in: "query", required: false, schema: { type: "string", enum: ["acoustic", "speaker", "semantic"] }, description: "Canonical required role selector; JSON body.kind is accepted for compatibility." }],
|
|
828441
828585
|
requestBody: { required: false, content: { "application/json": { schema: { type: "object", properties: { kind: { type: "string", enum: ["acoustic", "speaker", "semantic"] } } } } } },
|
|
828442
828586
|
responses: { 200: { description: "Requested role runtime provisioned and warm." }, 202: { description: "Provisioning finished but requested role did not prove warm; poll its health URL." }, 400: { description: "kind is missing or invalid." }, 403: { description: "Admin scope required." }, 500: { description: "Role-specific setup/preflight failed." } }
|
|
@@ -830030,6 +830174,71 @@ function getBackendTimeoutMs(perRequestSeconds) {
|
|
|
830030
830174
|
}
|
|
830031
830175
|
return BACKEND_TIMEOUT_DEFAULT_MS;
|
|
830032
830176
|
}
|
|
830177
|
+
function getAgentLoopToolTimeoutMs() {
|
|
830178
|
+
const configured = Number(process.env["OMNIUS_AGENT_LOOP_TOOL_TIMEOUT_MS"]);
|
|
830179
|
+
if (!Number.isFinite(configured) || configured <= 0) {
|
|
830180
|
+
return AGENT_LOOP_TOOL_TIMEOUT_DEFAULT_MS;
|
|
830181
|
+
}
|
|
830182
|
+
return Math.max(1e3, Math.min(Math.floor(configured), AGENT_LOOP_TOOL_TIMEOUT_MAX_MS));
|
|
830183
|
+
}
|
|
830184
|
+
function cancelAgentLoopTool(tool) {
|
|
830185
|
+
if (!tool || typeof tool.cancel !== "function" || cancelledAgentLoopTools.has(tool)) {
|
|
830186
|
+
return;
|
|
830187
|
+
}
|
|
830188
|
+
cancelledAgentLoopTools.add(tool);
|
|
830189
|
+
try {
|
|
830190
|
+
const cancelled = tool.cancel();
|
|
830191
|
+
if (cancelled && typeof cancelled.then === "function") {
|
|
830192
|
+
void Promise.resolve(cancelled).catch(() => void 0);
|
|
830193
|
+
}
|
|
830194
|
+
} catch {
|
|
830195
|
+
}
|
|
830196
|
+
}
|
|
830197
|
+
async function executeAgentLoopToolBounded(tool, args, timeoutMs, clientDisconnect) {
|
|
830198
|
+
const startedAt2 = performance.now();
|
|
830199
|
+
const execution = Promise.resolve().then(() => tool.execute(args)).then(
|
|
830200
|
+
(result) => ({ kind: "result", result }),
|
|
830201
|
+
(error) => ({ kind: "error", error })
|
|
830202
|
+
);
|
|
830203
|
+
let timer;
|
|
830204
|
+
try {
|
|
830205
|
+
const outcome = await Promise.race([
|
|
830206
|
+
execution,
|
|
830207
|
+
new Promise((resolveTimeout) => {
|
|
830208
|
+
timer = setTimeout(() => resolveTimeout({ kind: "timeout" }), timeoutMs);
|
|
830209
|
+
timer.unref();
|
|
830210
|
+
}),
|
|
830211
|
+
clientDisconnect.then(() => ({ kind: "client_disconnected" }))
|
|
830212
|
+
]);
|
|
830213
|
+
if (outcome.kind === "result") {
|
|
830214
|
+
return { result: outcome.result, clientDisconnected: false };
|
|
830215
|
+
}
|
|
830216
|
+
if (outcome.kind === "error") throw outcome.error;
|
|
830217
|
+
cancelAgentLoopTool(tool);
|
|
830218
|
+
if (outcome.kind === "client_disconnected") {
|
|
830219
|
+
return {
|
|
830220
|
+
result: {
|
|
830221
|
+
success: false,
|
|
830222
|
+
output: "",
|
|
830223
|
+
error: "agent_loop_client_disconnected",
|
|
830224
|
+
durationMs: performance.now() - startedAt2
|
|
830225
|
+
},
|
|
830226
|
+
clientDisconnected: true
|
|
830227
|
+
};
|
|
830228
|
+
}
|
|
830229
|
+
return {
|
|
830230
|
+
result: {
|
|
830231
|
+
success: false,
|
|
830232
|
+
output: "",
|
|
830233
|
+
error: `agent_loop_tool_timeout: daemon tool exceeded ${timeoutMs}ms`,
|
|
830234
|
+
durationMs: performance.now() - startedAt2
|
|
830235
|
+
},
|
|
830236
|
+
clientDisconnected: false
|
|
830237
|
+
};
|
|
830238
|
+
} finally {
|
|
830239
|
+
if (timer) clearTimeout(timer);
|
|
830240
|
+
}
|
|
830241
|
+
}
|
|
830033
830242
|
function getModelListTimeoutMs() {
|
|
830034
830243
|
const envS = process.env["OMNIUS_MODEL_LIST_TIMEOUT_S"];
|
|
830035
830244
|
if (envS) {
|
|
@@ -832379,361 +832588,447 @@ async function handleApiTags(res) {
|
|
|
832379
832588
|
}
|
|
832380
832589
|
async function runAgentLoopChatCompletions(opts) {
|
|
832381
832590
|
const { req: req3, res, ollamaUrl, requestBody, perReqTimeoutS } = opts;
|
|
832382
|
-
|
|
832383
|
-
|
|
832384
|
-
|
|
832385
|
-
const
|
|
832386
|
-
|
|
832387
|
-
|
|
832388
|
-
const
|
|
832389
|
-
const
|
|
832390
|
-
|
|
832391
|
-
|
|
832392
|
-
|
|
832393
|
-
|
|
832394
|
-
|
|
832395
|
-
const
|
|
832396
|
-
|
|
832397
|
-
|
|
832398
|
-
|
|
832399
|
-
|
|
832400
|
-
|
|
832401
|
-
|
|
832402
|
-
|
|
832403
|
-
|
|
832404
|
-
|
|
832405
|
-
|
|
832406
|
-
|
|
832407
|
-
|
|
832408
|
-
|
|
832409
|
-
|
|
832410
|
-
|
|
832411
|
-
|
|
832412
|
-
|
|
832413
|
-
|
|
832414
|
-
|
|
832415
|
-
|
|
832416
|
-
|
|
832417
|
-
|
|
832418
|
-
|
|
832419
|
-
|
|
832420
|
-
|
|
832421
|
-
|
|
832422
|
-
|
|
832423
|
-
const
|
|
832424
|
-
|
|
832425
|
-
|
|
832426
|
-
|
|
832427
|
-
|
|
832428
|
-
|
|
832591
|
+
let activeTool = null;
|
|
832592
|
+
let clientDisconnected = false;
|
|
832593
|
+
let resolveClientDisconnect;
|
|
832594
|
+
const clientDisconnect = new Promise((resolveDisconnect) => {
|
|
832595
|
+
resolveClientDisconnect = resolveDisconnect;
|
|
832596
|
+
});
|
|
832597
|
+
const cancelActiveTool = () => cancelAgentLoopTool(activeTool);
|
|
832598
|
+
const markClientDisconnected = () => {
|
|
832599
|
+
if (clientDisconnected) return;
|
|
832600
|
+
clientDisconnected = true;
|
|
832601
|
+
cancelActiveTool();
|
|
832602
|
+
resolveClientDisconnect();
|
|
832603
|
+
};
|
|
832604
|
+
const isClientDisconnected = () => {
|
|
832605
|
+
if (!clientDisconnected && (req3.aborted || res.destroyed && !res.writableEnded)) {
|
|
832606
|
+
markClientDisconnected();
|
|
832607
|
+
}
|
|
832608
|
+
return clientDisconnected;
|
|
832609
|
+
};
|
|
832610
|
+
const onRequestAborted = () => markClientDisconnected();
|
|
832611
|
+
const onRequestClose = () => {
|
|
832612
|
+
if (!req3.complete && !res.writableEnded) markClientDisconnected();
|
|
832613
|
+
};
|
|
832614
|
+
const onResponseClose = () => {
|
|
832615
|
+
if (!res.writableEnded) markClientDisconnected();
|
|
832616
|
+
};
|
|
832617
|
+
const onRequestError = () => markClientDisconnected();
|
|
832618
|
+
const onResponseError = () => markClientDisconnected();
|
|
832619
|
+
req3.once("aborted", onRequestAborted);
|
|
832620
|
+
req3.once("close", onRequestClose);
|
|
832621
|
+
req3.once("error", onRequestError);
|
|
832622
|
+
res.once("close", onResponseClose);
|
|
832623
|
+
res.once("error", onResponseError);
|
|
832624
|
+
try {
|
|
832625
|
+
const startMs = Date.now();
|
|
832626
|
+
const reqTimeoutMs = getBackendTimeoutMs(perReqTimeoutS);
|
|
832627
|
+
const totalDeadline = startMs + AGENT_LOOP_TOTAL_MAX_MS;
|
|
832628
|
+
const model = requestBody["model"] || "unknown";
|
|
832629
|
+
const route = resolveModelEndpoint(model);
|
|
832630
|
+
const targetUrl = route?.endpoint.url ?? ollamaUrl;
|
|
832631
|
+
const targetType = route?.endpoint.type ?? loadConfig().backendType ?? "ollama";
|
|
832632
|
+
const originalModel = route?.originalId ?? model;
|
|
832633
|
+
const maxTurns = typeof requestBody["max_turns"] === "number" ? Math.max(1, Math.min(64, requestBody["max_turns"])) : 8;
|
|
832634
|
+
const remoteIp = (req3.socket?.remoteAddress || "").replace(/^::ffff:/, "");
|
|
832635
|
+
const origin = /^(127\.\d+\.\d+\.\d+|::1|localhost)$/.test(remoteIp) ? "loopback" : "remote";
|
|
832636
|
+
const reqAuth = req3;
|
|
832637
|
+
const scope = reqAuth._authScope ?? (origin === "loopback" ? "admin" : "read");
|
|
832638
|
+
const profileName = typeof requestBody["profile"] === "string" ? requestBody["profile"] : typeof req3.headers["x-tool-profile"] === "string" ? req3.headers["x-tool-profile"] : req3._authProfile;
|
|
832639
|
+
const activeProfileMeta = profileName ? loadProfileWithMeta(
|
|
832640
|
+
profileName,
|
|
832641
|
+
typeof req3.headers["x-profile-password"] === "string" ? req3.headers["x-profile-password"] : void 0,
|
|
832642
|
+
process.cwd()
|
|
832643
|
+
) : null;
|
|
832644
|
+
if (profileName && !activeProfileMeta) {
|
|
832645
|
+
jsonResponse(res, 400, {
|
|
832646
|
+
error: "Profile not found or wrong password",
|
|
832647
|
+
profile: profileName,
|
|
832648
|
+
search_order: [
|
|
832649
|
+
"preset",
|
|
832650
|
+
`${process.cwd()}/.omnius/profiles/${profileName}.json`,
|
|
832651
|
+
"~/.omnius/profiles"
|
|
832652
|
+
]
|
|
832653
|
+
});
|
|
832654
|
+
return;
|
|
832655
|
+
}
|
|
832656
|
+
const execMod = await Promise.resolve().then(() => (init_dist5(), dist_exports2)).catch(() => null);
|
|
832657
|
+
if (isClientDisconnected()) return;
|
|
832658
|
+
if (!execMod) {
|
|
832659
|
+
jsonResponse(res, 500, { error: "Execution module unavailable" });
|
|
832660
|
+
return;
|
|
832661
|
+
}
|
|
832662
|
+
const classify2 = execMod.classifyTool;
|
|
832663
|
+
const canInvoke = execMod.canInvokeTool;
|
|
832664
|
+
const daemonTools = /* @__PURE__ */ new Map();
|
|
832665
|
+
for (const [classKey, value2] of Object.entries(execMod)) {
|
|
832666
|
+
if (typeof value2 !== "function") continue;
|
|
832667
|
+
const proto = value2.prototype;
|
|
832668
|
+
if (!proto || typeof proto.execute !== "function") continue;
|
|
832669
|
+
let probe = null;
|
|
832429
832670
|
try {
|
|
832430
|
-
probe = new value2(
|
|
832671
|
+
probe = new value2();
|
|
832431
832672
|
} catch {
|
|
832432
|
-
|
|
832673
|
+
try {
|
|
832674
|
+
probe = new value2(process.cwd());
|
|
832675
|
+
} catch {
|
|
832676
|
+
probe = null;
|
|
832677
|
+
}
|
|
832678
|
+
}
|
|
832679
|
+
if (probe?.name && typeof probe.name === "string") {
|
|
832680
|
+
daemonTools.set(probe.name, { ToolClass: value2, classKey });
|
|
832433
832681
|
}
|
|
832434
832682
|
}
|
|
832435
|
-
|
|
832436
|
-
|
|
832437
|
-
|
|
832438
|
-
|
|
832439
|
-
|
|
832440
|
-
|
|
832441
|
-
|
|
832442
|
-
|
|
832443
|
-
|
|
832444
|
-
|
|
832445
|
-
|
|
832446
|
-
|
|
832447
|
-
|
|
832448
|
-
|
|
832449
|
-
|
|
832450
|
-
if (!includeDaemonTools.includes(sec.requires_scope)) continue;
|
|
832451
|
-
if (canInvoke({ toolName, origin, scope }) !== null) continue;
|
|
832452
|
-
advertisedDaemonNames.add(toolName);
|
|
832683
|
+
const includeDaemonTools = Array.isArray(requestBody["include_daemon_tools"]) ? requestBody["include_daemon_tools"].filter(
|
|
832684
|
+
(s2) => typeof s2 === "string"
|
|
832685
|
+
) : [];
|
|
832686
|
+
const advertisedDaemonNames = /* @__PURE__ */ new Set();
|
|
832687
|
+
if (includeDaemonTools.length > 0) {
|
|
832688
|
+
for (const [toolName, { ToolClass: _T }] of daemonTools.entries()) {
|
|
832689
|
+
void _T;
|
|
832690
|
+
const sec = classify2(toolName);
|
|
832691
|
+
if (!sec) continue;
|
|
832692
|
+
if (activeProfileMeta && !isToolAllowed(activeProfileMeta.profile, toolName))
|
|
832693
|
+
continue;
|
|
832694
|
+
if (!includeDaemonTools.includes(sec.requires_scope)) continue;
|
|
832695
|
+
if (canInvoke({ toolName, origin, scope }) !== null) continue;
|
|
832696
|
+
advertisedDaemonNames.add(toolName);
|
|
832697
|
+
}
|
|
832453
832698
|
}
|
|
832454
|
-
|
|
832455
|
-
|
|
832456
|
-
|
|
832457
|
-
|
|
832458
|
-
|
|
832459
|
-
|
|
832460
|
-
|
|
832461
|
-
|
|
832462
|
-
|
|
832463
|
-
|
|
832464
|
-
let inst = null;
|
|
832465
|
-
try {
|
|
832466
|
-
inst = new meta.ToolClass();
|
|
832467
|
-
} catch {
|
|
832699
|
+
const callerTools = Array.isArray(requestBody["tools"]) ? requestBody["tools"].filter(
|
|
832700
|
+
(t2) => t2?.type === "function" && typeof t2?.function?.name === "string"
|
|
832701
|
+
) : [];
|
|
832702
|
+
const callerToolNames = new Set(callerTools.map((t2) => t2.function.name));
|
|
832703
|
+
const daemonToolEntries = [];
|
|
832704
|
+
for (const name10 of advertisedDaemonNames) {
|
|
832705
|
+
if (callerToolNames.has(name10)) continue;
|
|
832706
|
+
const meta = daemonTools.get(name10);
|
|
832707
|
+
if (!meta) continue;
|
|
832708
|
+
let inst = null;
|
|
832468
832709
|
try {
|
|
832469
|
-
inst = new meta.ToolClass(
|
|
832710
|
+
inst = new meta.ToolClass();
|
|
832470
832711
|
} catch {
|
|
832471
|
-
|
|
832712
|
+
try {
|
|
832713
|
+
inst = new meta.ToolClass(process.cwd());
|
|
832714
|
+
} catch {
|
|
832715
|
+
inst = null;
|
|
832716
|
+
}
|
|
832472
832717
|
}
|
|
832718
|
+
if (!inst) continue;
|
|
832719
|
+
daemonToolEntries.push({
|
|
832720
|
+
type: "function",
|
|
832721
|
+
function: {
|
|
832722
|
+
name: name10,
|
|
832723
|
+
description: typeof inst.description === "string" ? inst.description : "",
|
|
832724
|
+
parameters: inst.parameters ?? { type: "object", properties: {} }
|
|
832725
|
+
}
|
|
832726
|
+
});
|
|
832473
832727
|
}
|
|
832474
|
-
|
|
832475
|
-
|
|
832476
|
-
|
|
832477
|
-
|
|
832478
|
-
|
|
832479
|
-
|
|
832480
|
-
|
|
832481
|
-
|
|
832482
|
-
|
|
832483
|
-
|
|
832484
|
-
|
|
832485
|
-
|
|
832486
|
-
|
|
832487
|
-
|
|
832488
|
-
|
|
832489
|
-
"
|
|
832490
|
-
|
|
832491
|
-
|
|
832492
|
-
|
|
832493
|
-
|
|
832494
|
-
|
|
832495
|
-
"on the most relevant URL, then synthesize a grounded answer citing",
|
|
832496
|
-
"the source. Decline to answer factual questions if web_search is",
|
|
832497
|
-
"unavailable."
|
|
832498
|
-
].join(" ");
|
|
832499
|
-
const firstSystemIdx = messages2.findIndex((m2) => m2.role === "system");
|
|
832500
|
-
if (firstSystemIdx >= 0 && typeof messages2[firstSystemIdx].content === "string") {
|
|
832501
|
-
messages2[firstSystemIdx] = {
|
|
832502
|
-
...messages2[firstSystemIdx],
|
|
832503
|
-
content: `${SYSTEM_FACTUAL_FIRST}
|
|
832728
|
+
const mergedTools = [...callerTools, ...daemonToolEntries];
|
|
832729
|
+
const forceNoThinkForTools = targetType === "ollama" && mergedTools.length > 0;
|
|
832730
|
+
const messages2 = Array.isArray(requestBody["messages"]) ? requestBody["messages"].slice() : [];
|
|
832731
|
+
const promptTemplate = typeof requestBody["prompt_template"] === "string" ? requestBody["prompt_template"] : null;
|
|
832732
|
+
if (promptTemplate === "factual-first" && advertisedDaemonNames.has("web_search")) {
|
|
832733
|
+
const SYSTEM_FACTUAL_FIRST = [
|
|
832734
|
+
"[POLICY: factual-first]",
|
|
832735
|
+
"For any user question that asks for a fact, statistic, current event,",
|
|
832736
|
+
"person/place/product detail, or anything you might be tempted to answer",
|
|
832737
|
+
"from training data alone — your FIRST tool_call MUST be `web_search`",
|
|
832738
|
+
"with a query derived from the user's question. Do NOT answer from",
|
|
832739
|
+
"training data. After web_search returns, optionally call `web_fetch`",
|
|
832740
|
+
"on the most relevant URL, then synthesize a grounded answer citing",
|
|
832741
|
+
"the source. Decline to answer factual questions if web_search is",
|
|
832742
|
+
"unavailable."
|
|
832743
|
+
].join(" ");
|
|
832744
|
+
const firstSystemIdx = messages2.findIndex((m2) => m2.role === "system");
|
|
832745
|
+
if (firstSystemIdx >= 0 && typeof messages2[firstSystemIdx].content === "string") {
|
|
832746
|
+
messages2[firstSystemIdx] = {
|
|
832747
|
+
...messages2[firstSystemIdx],
|
|
832748
|
+
content: `${SYSTEM_FACTUAL_FIRST}
|
|
832504
832749
|
|
|
832505
832750
|
${messages2[firstSystemIdx].content}`
|
|
832506
|
-
|
|
832507
|
-
|
|
832508
|
-
|
|
832509
|
-
|
|
832510
|
-
}
|
|
832511
|
-
const chatId = `chatcmpl-${randomBytes30(12).toString("hex")}`;
|
|
832512
|
-
const turnsLog = [];
|
|
832513
|
-
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
832514
|
-
if (Date.now() > totalDeadline) {
|
|
832515
|
-
jsonResponse(res, 504, {
|
|
832516
|
-
error: "agent_loop timed out",
|
|
832517
|
-
turn,
|
|
832518
|
-
elapsed_ms: Date.now() - startMs,
|
|
832519
|
-
timeout_s: perReqTimeoutS ?? null
|
|
832520
|
-
});
|
|
832521
|
-
return;
|
|
832751
|
+
};
|
|
832752
|
+
} else {
|
|
832753
|
+
messages2.unshift({ role: "system", content: SYSTEM_FACTUAL_FIRST });
|
|
832754
|
+
}
|
|
832522
832755
|
}
|
|
832523
|
-
const
|
|
832524
|
-
|
|
832525
|
-
|
|
832526
|
-
|
|
832527
|
-
|
|
832528
|
-
|
|
832529
|
-
|
|
832530
|
-
agent_loop: void 0,
|
|
832531
|
-
include_daemon_tools: void 0,
|
|
832532
|
-
max_turns: void 0,
|
|
832533
|
-
timeout_s: void 0,
|
|
832534
|
-
realtime: void 0,
|
|
832535
|
-
realtime_options: void 0,
|
|
832536
|
-
realtime_max_history_messages: void 0,
|
|
832537
|
-
realtime_max_tokens: void 0
|
|
832538
|
-
};
|
|
832539
|
-
for (const k of Object.keys(turnBody))
|
|
832540
|
-
if (turnBody[k] === void 0) delete turnBody[k];
|
|
832541
|
-
let parsed;
|
|
832542
|
-
try {
|
|
832543
|
-
const path16 = targetType === "vllm" || targetType === "openai" ? "/v1/chat/completions" : "/v1/chat/completions";
|
|
832544
|
-
const result = await ollamaRequest(
|
|
832545
|
-
targetUrl,
|
|
832546
|
-
path16,
|
|
832547
|
-
"POST",
|
|
832548
|
-
JSON.stringify(turnBody),
|
|
832549
|
-
reqTimeoutMs,
|
|
832550
|
-
route?.endpoint
|
|
832551
|
-
);
|
|
832552
|
-
if (result.status !== 200) {
|
|
832553
|
-
jsonResponse(res, result.status, {
|
|
832554
|
-
error: "Backend request failed during agent_loop",
|
|
832756
|
+
const chatId = `chatcmpl-${randomBytes30(12).toString("hex")}`;
|
|
832757
|
+
const turnsLog = [];
|
|
832758
|
+
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
832759
|
+
if (isClientDisconnected()) return;
|
|
832760
|
+
if (Date.now() > totalDeadline) {
|
|
832761
|
+
jsonResponse(res, 504, {
|
|
832762
|
+
error: "agent_loop timed out",
|
|
832555
832763
|
turn,
|
|
832556
|
-
|
|
832764
|
+
elapsed_ms: Date.now() - startMs,
|
|
832765
|
+
timeout_s: perReqTimeoutS ?? null
|
|
832557
832766
|
});
|
|
832558
832767
|
return;
|
|
832559
832768
|
}
|
|
832560
|
-
|
|
832561
|
-
|
|
832562
|
-
|
|
832563
|
-
|
|
832564
|
-
|
|
832565
|
-
|
|
832566
|
-
|
|
832567
|
-
|
|
832568
|
-
|
|
832569
|
-
const choice = parsed?.choices?.[0];
|
|
832570
|
-
const message2 = choice?.message || { role: "assistant", content: "" };
|
|
832571
|
-
const toolCalls = Array.isArray(message2.tool_calls) ? message2.tool_calls : [];
|
|
832572
|
-
if (toolCalls.length === 0) {
|
|
832573
|
-
jsonResponse(res, 200, {
|
|
832574
|
-
...parsed,
|
|
832575
|
-
id: chatId,
|
|
832576
|
-
_agent_loop: {
|
|
832577
|
-
turns: turn,
|
|
832578
|
-
log: turnsLog,
|
|
832579
|
-
done: true,
|
|
832580
|
-
reason: "model_returned_content"
|
|
832581
|
-
}
|
|
832582
|
-
});
|
|
832583
|
-
return;
|
|
832584
|
-
}
|
|
832585
|
-
const daemonCalls = [];
|
|
832586
|
-
const clientCalls = [];
|
|
832587
|
-
for (const tc of toolCalls) {
|
|
832588
|
-
const name10 = tc?.function?.name ?? tc?.name ?? "";
|
|
832589
|
-
if (advertisedDaemonNames.has(name10)) daemonCalls.push(tc);
|
|
832590
|
-
else clientCalls.push(tc);
|
|
832591
|
-
}
|
|
832592
|
-
if (clientCalls.length > 0) {
|
|
832593
|
-
turnsLog.push({
|
|
832594
|
-
turn,
|
|
832595
|
-
tool_calls: toolCalls.length,
|
|
832596
|
-
daemon_executed: 0,
|
|
832597
|
-
client_yielded: clientCalls.length
|
|
832598
|
-
});
|
|
832599
|
-
jsonResponse(res, 200, {
|
|
832600
|
-
...parsed,
|
|
832601
|
-
id: chatId,
|
|
832602
|
-
_agent_loop: {
|
|
832603
|
-
turns: turn,
|
|
832604
|
-
log: turnsLog,
|
|
832605
|
-
done: false,
|
|
832606
|
-
reason: "client_tools_pending",
|
|
832607
|
-
pending_tool_calls: clientCalls.length
|
|
832608
|
-
}
|
|
832609
|
-
});
|
|
832610
|
-
return;
|
|
832611
|
-
}
|
|
832612
|
-
messages2.push(message2);
|
|
832613
|
-
let executed = 0;
|
|
832614
|
-
for (const tc of daemonCalls) {
|
|
832615
|
-
const name10 = tc?.function?.name ?? tc?.name ?? "";
|
|
832616
|
-
const argsRaw = tc?.function?.arguments ?? tc?.arguments ?? "{}";
|
|
832617
|
-
let args = {};
|
|
832618
|
-
try {
|
|
832619
|
-
args = typeof argsRaw === "string" ? JSON.parse(argsRaw) : argsRaw || {};
|
|
832620
|
-
} catch {
|
|
832621
|
-
args = {};
|
|
832769
|
+
const remainingMs = totalDeadline - Date.now();
|
|
832770
|
+
if (remainingMs <= 0) {
|
|
832771
|
+
jsonResponse(res, 504, {
|
|
832772
|
+
error: "agent_loop timed out",
|
|
832773
|
+
turn,
|
|
832774
|
+
elapsed_ms: Date.now() - startMs,
|
|
832775
|
+
timeout_s: perReqTimeoutS ?? null
|
|
832776
|
+
});
|
|
832777
|
+
return;
|
|
832622
832778
|
}
|
|
832623
|
-
const
|
|
832624
|
-
|
|
832625
|
-
|
|
832626
|
-
|
|
832627
|
-
|
|
832628
|
-
|
|
832629
|
-
|
|
832630
|
-
|
|
832631
|
-
|
|
832632
|
-
|
|
832779
|
+
const turnBody = {
|
|
832780
|
+
...requestBody,
|
|
832781
|
+
model: originalModel,
|
|
832782
|
+
messages: forceNoThinkForTools ? messages2.map((message3) => typeof message3.content === "string" ? { ...message3, content: stripNoThinkPromptDirectives(message3.content) } : message3) : messages2,
|
|
832783
|
+
tools: mergedTools.length > 0 ? mergedTools : void 0,
|
|
832784
|
+
stream: false,
|
|
832785
|
+
...forceNoThinkForTools ? { think: false } : {},
|
|
832786
|
+
// Strip our own loop-control fields so they don't confuse the backend
|
|
832787
|
+
agent_loop: void 0,
|
|
832788
|
+
include_daemon_tools: void 0,
|
|
832789
|
+
max_turns: void 0,
|
|
832790
|
+
timeout_s: void 0,
|
|
832791
|
+
realtime: void 0,
|
|
832792
|
+
realtime_options: void 0,
|
|
832793
|
+
realtime_max_history_messages: void 0,
|
|
832794
|
+
realtime_max_tokens: void 0
|
|
832795
|
+
};
|
|
832796
|
+
for (const k of Object.keys(turnBody))
|
|
832797
|
+
if (turnBody[k] === void 0) delete turnBody[k];
|
|
832798
|
+
let parsed;
|
|
832799
|
+
try {
|
|
832800
|
+
const path16 = targetType === "vllm" || targetType === "openai" ? "/v1/chat/completions" : "/v1/chat/completions";
|
|
832801
|
+
const result = await ollamaRequest(
|
|
832802
|
+
targetUrl,
|
|
832803
|
+
path16,
|
|
832804
|
+
"POST",
|
|
832805
|
+
JSON.stringify(turnBody),
|
|
832806
|
+
Math.min(reqTimeoutMs, remainingMs),
|
|
832807
|
+
route?.endpoint
|
|
832808
|
+
);
|
|
832809
|
+
if (isClientDisconnected()) return;
|
|
832810
|
+
if (result.status !== 200) {
|
|
832811
|
+
jsonResponse(res, result.status, {
|
|
832812
|
+
error: "Backend request failed during agent_loop",
|
|
832813
|
+
turn,
|
|
832814
|
+
details: result.body.slice(0, 500)
|
|
832815
|
+
});
|
|
832816
|
+
return;
|
|
832817
|
+
}
|
|
832818
|
+
parsed = JSON.parse(result.body);
|
|
832819
|
+
} catch (err) {
|
|
832820
|
+
if (isClientDisconnected()) return;
|
|
832821
|
+
jsonResponse(res, 502, {
|
|
832822
|
+
error: "Backend proxy error during agent_loop",
|
|
832823
|
+
message: err instanceof Error ? err.message : String(err),
|
|
832824
|
+
turn
|
|
832633
832825
|
});
|
|
832634
|
-
|
|
832826
|
+
return;
|
|
832635
832827
|
}
|
|
832636
|
-
|
|
832637
|
-
|
|
832638
|
-
|
|
832639
|
-
|
|
832640
|
-
|
|
832641
|
-
|
|
832642
|
-
|
|
832643
|
-
|
|
832644
|
-
|
|
832645
|
-
|
|
832646
|
-
|
|
832647
|
-
|
|
832648
|
-
|
|
832649
|
-
|
|
832650
|
-
|
|
832651
|
-
|
|
832652
|
-
|
|
832653
|
-
|
|
832828
|
+
const choice = parsed?.choices?.[0];
|
|
832829
|
+
const rawMessage = choice?.message || {};
|
|
832830
|
+
const message2 = {
|
|
832831
|
+
...rawMessage,
|
|
832832
|
+
role: typeof rawMessage.role === "string" ? rawMessage.role : "assistant",
|
|
832833
|
+
// Tool-only OpenAI/Ollama turns can return null content. Preserve the
|
|
832834
|
+
// tool_calls but normalize content for the next protocol turn.
|
|
832835
|
+
content: typeof rawMessage.content === "string" ? rawMessage.content : ""
|
|
832836
|
+
};
|
|
832837
|
+
const toolCalls = Array.isArray(message2.tool_calls) ? message2.tool_calls : [];
|
|
832838
|
+
if (toolCalls.length === 0) {
|
|
832839
|
+
jsonResponse(res, 200, {
|
|
832840
|
+
...parsed,
|
|
832841
|
+
id: chatId,
|
|
832842
|
+
_agent_loop: {
|
|
832843
|
+
turns: turn,
|
|
832844
|
+
log: turnsLog,
|
|
832845
|
+
done: true,
|
|
832846
|
+
reason: "model_returned_content"
|
|
832847
|
+
}
|
|
832654
832848
|
});
|
|
832655
|
-
|
|
832849
|
+
return;
|
|
832656
832850
|
}
|
|
832657
|
-
const
|
|
832658
|
-
|
|
832659
|
-
|
|
832660
|
-
|
|
832661
|
-
|
|
832662
|
-
|
|
832663
|
-
|
|
832851
|
+
const daemonCalls = [];
|
|
832852
|
+
const clientCalls = [];
|
|
832853
|
+
for (const tc of toolCalls) {
|
|
832854
|
+
const name10 = tc?.function?.name ?? tc?.name ?? "";
|
|
832855
|
+
if (advertisedDaemonNames.has(name10)) daemonCalls.push(tc);
|
|
832856
|
+
else clientCalls.push(tc);
|
|
832857
|
+
}
|
|
832858
|
+
if (clientCalls.length > 0) {
|
|
832859
|
+
turnsLog.push({
|
|
832860
|
+
turn,
|
|
832861
|
+
tool_calls: toolCalls.length,
|
|
832862
|
+
daemon_executed: 0,
|
|
832863
|
+
client_yielded: clientCalls.length
|
|
832664
832864
|
});
|
|
832665
|
-
|
|
832865
|
+
jsonResponse(res, 200, {
|
|
832866
|
+
...parsed,
|
|
832867
|
+
id: chatId,
|
|
832868
|
+
_agent_loop: {
|
|
832869
|
+
turns: turn,
|
|
832870
|
+
log: turnsLog,
|
|
832871
|
+
done: false,
|
|
832872
|
+
reason: "client_tools_pending",
|
|
832873
|
+
pending_tool_calls: clientCalls.length
|
|
832874
|
+
}
|
|
832875
|
+
});
|
|
832876
|
+
return;
|
|
832666
832877
|
}
|
|
832667
|
-
|
|
832668
|
-
|
|
832669
|
-
|
|
832670
|
-
|
|
832878
|
+
messages2.push(message2);
|
|
832879
|
+
let executed = 0;
|
|
832880
|
+
for (const tc of daemonCalls) {
|
|
832881
|
+
const name10 = tc?.function?.name ?? tc?.name ?? "";
|
|
832882
|
+
const argsRaw = tc?.function?.arguments ?? tc?.arguments ?? "{}";
|
|
832883
|
+
let args = {};
|
|
832671
832884
|
try {
|
|
832672
|
-
|
|
832885
|
+
args = typeof argsRaw === "string" ? JSON.parse(argsRaw) : argsRaw || {};
|
|
832673
832886
|
} catch {
|
|
832674
|
-
|
|
832887
|
+
args = {};
|
|
832888
|
+
}
|
|
832889
|
+
const denial = canInvoke({ toolName: name10, origin, scope });
|
|
832890
|
+
if (denial) {
|
|
832891
|
+
messages2.push({
|
|
832892
|
+
role: "tool",
|
|
832893
|
+
tool_call_id: tc.id,
|
|
832894
|
+
name: name10,
|
|
832895
|
+
content: JSON.stringify({
|
|
832896
|
+
error: denial.reason,
|
|
832897
|
+
status: denial.status
|
|
832898
|
+
})
|
|
832899
|
+
});
|
|
832900
|
+
continue;
|
|
832901
|
+
}
|
|
832902
|
+
if (activeProfileMeta && !isToolAllowed(activeProfileMeta.profile, name10)) {
|
|
832903
|
+
messages2.push({
|
|
832904
|
+
role: "tool",
|
|
832905
|
+
tool_call_id: tc.id,
|
|
832906
|
+
name: name10,
|
|
832907
|
+
content: JSON.stringify({
|
|
832908
|
+
success: false,
|
|
832909
|
+
error: `Tool '${name10}' is denied by profile '${activeProfileMeta.profile.name}'.`,
|
|
832910
|
+
active_profile: {
|
|
832911
|
+
name: activeProfileMeta.profile.name,
|
|
832912
|
+
source: activeProfileMeta.source,
|
|
832913
|
+
path: activeProfileMeta.path,
|
|
832914
|
+
allowed_tools: allowedToolsForProfile(
|
|
832915
|
+
activeProfileMeta.profile,
|
|
832916
|
+
daemonTools.keys()
|
|
832917
|
+
)
|
|
832918
|
+
}
|
|
832919
|
+
})
|
|
832920
|
+
});
|
|
832921
|
+
continue;
|
|
832922
|
+
}
|
|
832923
|
+
const meta = daemonTools.get(name10);
|
|
832924
|
+
if (!meta) {
|
|
832925
|
+
messages2.push({
|
|
832926
|
+
role: "tool",
|
|
832927
|
+
tool_call_id: tc.id,
|
|
832928
|
+
name: name10,
|
|
832929
|
+
content: JSON.stringify({ error: `Daemon tool '${name10}' not found` })
|
|
832930
|
+
});
|
|
832931
|
+
continue;
|
|
832932
|
+
}
|
|
832933
|
+
let tool = null;
|
|
832934
|
+
try {
|
|
832935
|
+
tool = new meta.ToolClass(process.cwd());
|
|
832936
|
+
} catch {
|
|
832937
|
+
try {
|
|
832938
|
+
tool = new meta.ToolClass();
|
|
832939
|
+
} catch {
|
|
832940
|
+
tool = null;
|
|
832941
|
+
}
|
|
832942
|
+
}
|
|
832943
|
+
if (!tool) {
|
|
832944
|
+
messages2.push({
|
|
832945
|
+
role: "tool",
|
|
832946
|
+
tool_call_id: tc.id,
|
|
832947
|
+
name: name10,
|
|
832948
|
+
content: JSON.stringify({
|
|
832949
|
+
error: `Could not instantiate ${meta.classKey}`
|
|
832950
|
+
})
|
|
832951
|
+
});
|
|
832952
|
+
continue;
|
|
832953
|
+
}
|
|
832954
|
+
if (isClientDisconnected()) return;
|
|
832955
|
+
const remainingToolMs = totalDeadline - Date.now();
|
|
832956
|
+
if (remainingToolMs <= 0) {
|
|
832957
|
+
jsonResponse(res, 504, {
|
|
832958
|
+
error: "agent_loop timed out",
|
|
832959
|
+
turn,
|
|
832960
|
+
elapsed_ms: Date.now() - startMs,
|
|
832961
|
+
timeout_s: perReqTimeoutS ?? null
|
|
832962
|
+
});
|
|
832963
|
+
return;
|
|
832964
|
+
}
|
|
832965
|
+
let toolResult;
|
|
832966
|
+
try {
|
|
832967
|
+
activeTool = tool;
|
|
832968
|
+
const execution = await executeAgentLoopToolBounded(
|
|
832969
|
+
tool,
|
|
832970
|
+
args,
|
|
832971
|
+
Math.min(getAgentLoopToolTimeoutMs(), remainingToolMs),
|
|
832972
|
+
clientDisconnect
|
|
832973
|
+
);
|
|
832974
|
+
if (execution.clientDisconnected || isClientDisconnected()) return;
|
|
832975
|
+
toolResult = execution.result;
|
|
832976
|
+
} catch (e2) {
|
|
832977
|
+
if (isClientDisconnected()) return;
|
|
832978
|
+
toolResult = {
|
|
832979
|
+
success: false,
|
|
832980
|
+
output: "",
|
|
832981
|
+
error: e2 instanceof Error ? e2.message : String(e2),
|
|
832982
|
+
durationMs: 0
|
|
832983
|
+
};
|
|
832984
|
+
} finally {
|
|
832985
|
+
activeTool = null;
|
|
832675
832986
|
}
|
|
832676
|
-
}
|
|
832677
|
-
if (!tool) {
|
|
832678
832987
|
messages2.push({
|
|
832679
832988
|
role: "tool",
|
|
832680
832989
|
tool_call_id: tc.id,
|
|
832681
832990
|
name: name10,
|
|
832682
|
-
content: JSON.stringify(
|
|
832683
|
-
error: `Could not instantiate ${meta.classKey}`
|
|
832684
|
-
})
|
|
832991
|
+
content: JSON.stringify(toolResult)
|
|
832685
832992
|
});
|
|
832686
|
-
|
|
832993
|
+
executed++;
|
|
832687
832994
|
}
|
|
832688
|
-
|
|
832689
|
-
|
|
832690
|
-
|
|
832691
|
-
|
|
832692
|
-
|
|
832693
|
-
success: false,
|
|
832694
|
-
output: "",
|
|
832695
|
-
error: e2 instanceof Error ? e2.message : String(e2),
|
|
832696
|
-
durationMs: 0
|
|
832697
|
-
};
|
|
832698
|
-
}
|
|
832699
|
-
messages2.push({
|
|
832700
|
-
role: "tool",
|
|
832701
|
-
tool_call_id: tc.id,
|
|
832702
|
-
name: name10,
|
|
832703
|
-
content: JSON.stringify(toolResult)
|
|
832995
|
+
turnsLog.push({
|
|
832996
|
+
turn,
|
|
832997
|
+
tool_calls: toolCalls.length,
|
|
832998
|
+
daemon_executed: executed,
|
|
832999
|
+
client_yielded: 0
|
|
832704
833000
|
});
|
|
832705
|
-
executed++;
|
|
832706
833001
|
}
|
|
832707
|
-
|
|
832708
|
-
|
|
832709
|
-
|
|
832710
|
-
|
|
832711
|
-
|
|
833002
|
+
jsonResponse(res, 200, {
|
|
833003
|
+
id: chatId,
|
|
833004
|
+
object: "chat.completion",
|
|
833005
|
+
created: Math.floor(Date.now() / 1e3),
|
|
833006
|
+
model,
|
|
833007
|
+
choices: [
|
|
833008
|
+
{
|
|
833009
|
+
index: 0,
|
|
833010
|
+
message: {
|
|
833011
|
+
role: "assistant",
|
|
833012
|
+
content: "[agent_loop reached max_turns without producing a final answer]"
|
|
833013
|
+
},
|
|
833014
|
+
finish_reason: "length"
|
|
833015
|
+
}
|
|
833016
|
+
],
|
|
833017
|
+
_agent_loop: {
|
|
833018
|
+
turns: maxTurns,
|
|
833019
|
+
log: turnsLog,
|
|
833020
|
+
done: false,
|
|
833021
|
+
reason: "max_turns_exhausted",
|
|
833022
|
+
max_turns: maxTurns
|
|
833023
|
+
}
|
|
832712
833024
|
});
|
|
833025
|
+
} finally {
|
|
833026
|
+
req3.removeListener("aborted", onRequestAborted);
|
|
833027
|
+
req3.removeListener("close", onRequestClose);
|
|
833028
|
+
req3.removeListener("error", onRequestError);
|
|
833029
|
+
res.removeListener("close", onResponseClose);
|
|
833030
|
+
res.removeListener("error", onResponseError);
|
|
832713
833031
|
}
|
|
832714
|
-
jsonResponse(res, 200, {
|
|
832715
|
-
id: chatId,
|
|
832716
|
-
object: "chat.completion",
|
|
832717
|
-
created: Math.floor(Date.now() / 1e3),
|
|
832718
|
-
model,
|
|
832719
|
-
choices: [
|
|
832720
|
-
{
|
|
832721
|
-
index: 0,
|
|
832722
|
-
message: {
|
|
832723
|
-
role: "assistant",
|
|
832724
|
-
content: "[agent_loop reached max_turns without producing a final answer]"
|
|
832725
|
-
},
|
|
832726
|
-
finish_reason: "length"
|
|
832727
|
-
}
|
|
832728
|
-
],
|
|
832729
|
-
_agent_loop: {
|
|
832730
|
-
turns: maxTurns,
|
|
832731
|
-
log: turnsLog,
|
|
832732
|
-
done: false,
|
|
832733
|
-
reason: "max_turns_exhausted",
|
|
832734
|
-
max_turns: maxTurns
|
|
832735
|
-
}
|
|
832736
|
-
});
|
|
832737
833032
|
}
|
|
832738
833033
|
async function handleV1ChatCompletions(req3, res, ollamaUrl) {
|
|
832739
833034
|
const body = await parseJsonBody(req3);
|
|
@@ -841203,7 +841498,7 @@ function setTimerEnabled(name10, enabled2) {
|
|
|
841203
841498
|
return false;
|
|
841204
841499
|
}
|
|
841205
841500
|
}
|
|
841206
|
-
var require4, NEXUS_DIRECTORY_ORIGIN3, NEXUS_SPONSORS_URL3, SERVER_BOOT_IDENTITY, voiceTtsTransactionTail, endpointRegistry, modelRouteMap, endpointUsage, _lastEndpointDiagnostics, BACKEND_TIMEOUT_DEFAULT_MS, BACKEND_TIMEOUT_MAX_MS, MODEL_LIST_TIMEOUT_DEFAULT_MS, MODEL_CHECK_TIMEOUT_DEFAULT_MS, MODEL_CHECK_TIMEOUT_MAX_MS, MODEL_CHECK_MAX_TOKENS, metrics, startedAt, FILE_MIME_BY_EXT, realtimeOllamaFallbackCache, runningProcesses, LOCAL_UI_SESSION_COOKIE, LOCAL_UI_SESSION_TOKEN, perKeyUsage, CRON_MARKER2;
|
|
841501
|
+
var require4, NEXUS_DIRECTORY_ORIGIN3, NEXUS_SPONSORS_URL3, SERVER_BOOT_IDENTITY, voiceTtsTransactionTail, endpointRegistry, modelRouteMap, endpointUsage, _lastEndpointDiagnostics, BACKEND_TIMEOUT_DEFAULT_MS, BACKEND_TIMEOUT_MAX_MS, AGENT_LOOP_TOTAL_MAX_MS, AGENT_LOOP_TOOL_TIMEOUT_DEFAULT_MS, AGENT_LOOP_TOOL_TIMEOUT_MAX_MS, MODEL_LIST_TIMEOUT_DEFAULT_MS, MODEL_CHECK_TIMEOUT_DEFAULT_MS, MODEL_CHECK_TIMEOUT_MAX_MS, MODEL_CHECK_MAX_TOKENS, cancelledAgentLoopTools, metrics, startedAt, FILE_MIME_BY_EXT, realtimeOllamaFallbackCache, runningProcesses, LOCAL_UI_SESSION_COOKIE, LOCAL_UI_SESSION_TOKEN, perKeyUsage, CRON_MARKER2;
|
|
841207
841502
|
var init_serve = __esm({
|
|
841208
841503
|
"packages/cli/src/api/serve.ts"() {
|
|
841209
841504
|
init_config();
|
|
@@ -841259,10 +841554,14 @@ var init_serve = __esm({
|
|
|
841259
841554
|
_lastEndpointDiagnostics = [];
|
|
841260
841555
|
BACKEND_TIMEOUT_DEFAULT_MS = 12e4;
|
|
841261
841556
|
BACKEND_TIMEOUT_MAX_MS = 36e5;
|
|
841557
|
+
AGENT_LOOP_TOTAL_MAX_MS = 30 * 60 * 1e3;
|
|
841558
|
+
AGENT_LOOP_TOOL_TIMEOUT_DEFAULT_MS = 3e4;
|
|
841559
|
+
AGENT_LOOP_TOOL_TIMEOUT_MAX_MS = 12e4;
|
|
841262
841560
|
MODEL_LIST_TIMEOUT_DEFAULT_MS = 1500;
|
|
841263
841561
|
MODEL_CHECK_TIMEOUT_DEFAULT_MS = 1e4;
|
|
841264
841562
|
MODEL_CHECK_TIMEOUT_MAX_MS = 3e4;
|
|
841265
841563
|
MODEL_CHECK_MAX_TOKENS = 32;
|
|
841564
|
+
cancelledAgentLoopTools = /* @__PURE__ */ new WeakSet();
|
|
841266
841565
|
metrics = {
|
|
841267
841566
|
requests: /* @__PURE__ */ new Map(),
|
|
841268
841567
|
totalTokensIn: 0,
|