open-agents-ai 0.184.44 → 0.184.46
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 +208 -100
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -672,20 +672,20 @@ var init_VllmBackend = __esm({
|
|
|
672
672
|
// -------------------------------------------------------------------------
|
|
673
673
|
// LLMBackend — complete
|
|
674
674
|
// -------------------------------------------------------------------------
|
|
675
|
-
async complete(
|
|
676
|
-
const messages =
|
|
675
|
+
async complete(request) {
|
|
676
|
+
const messages = request.messages.map(agentMessageToChatMessage);
|
|
677
677
|
const body = {
|
|
678
678
|
model: this.model,
|
|
679
679
|
messages,
|
|
680
|
-
temperature:
|
|
681
|
-
max_tokens:
|
|
680
|
+
temperature: request.temperature ?? 0,
|
|
681
|
+
max_tokens: request.maxTokens ?? 4096
|
|
682
682
|
};
|
|
683
|
-
if (
|
|
684
|
-
body.tools =
|
|
683
|
+
if (request.tools && request.tools.length > 0) {
|
|
684
|
+
body.tools = request.tools;
|
|
685
685
|
body.tool_choice = "auto";
|
|
686
686
|
}
|
|
687
|
-
if (
|
|
688
|
-
body.response_format =
|
|
687
|
+
if (request.responseFormat) {
|
|
688
|
+
body.response_format = request.responseFormat;
|
|
689
689
|
}
|
|
690
690
|
const start = Date.now();
|
|
691
691
|
const rawResponse = await this.postWithRetry("/v1/chat/completions", body);
|
|
@@ -822,24 +822,24 @@ var init_OllamaBackend = __esm({
|
|
|
822
822
|
// -------------------------------------------------------------------------
|
|
823
823
|
// LLMBackend — complete
|
|
824
824
|
// -------------------------------------------------------------------------
|
|
825
|
-
async complete(
|
|
826
|
-
const messages =
|
|
825
|
+
async complete(request) {
|
|
826
|
+
const messages = request.messages.map(agentMessageToChatMessage2);
|
|
827
827
|
const body = {
|
|
828
828
|
model: this.model,
|
|
829
829
|
messages,
|
|
830
|
-
temperature:
|
|
831
|
-
max_tokens:
|
|
830
|
+
temperature: request.temperature ?? 0,
|
|
831
|
+
max_tokens: request.maxTokens ?? 4096,
|
|
832
832
|
// Disable Qwen thinking mode by default so tokens go to content,
|
|
833
833
|
// not reasoning. The model still produces good output without it
|
|
834
834
|
// and coding agents need structured JSON in the content field.
|
|
835
835
|
think: false
|
|
836
836
|
};
|
|
837
|
-
if (
|
|
838
|
-
body.tools =
|
|
837
|
+
if (request.tools && request.tools.length > 0) {
|
|
838
|
+
body.tools = request.tools;
|
|
839
839
|
body.tool_choice = "auto";
|
|
840
840
|
}
|
|
841
|
-
if (
|
|
842
|
-
body.response_format =
|
|
841
|
+
if (request.responseFormat) {
|
|
842
|
+
body.response_format = request.responseFormat;
|
|
843
843
|
}
|
|
844
844
|
const start = Date.now();
|
|
845
845
|
const rawResponse = await this.postWithRetry("/v1/chat/completions", body);
|
|
@@ -1003,19 +1003,19 @@ var init_FakeBackend = __esm({
|
|
|
1003
1003
|
this._healthCheckCount++;
|
|
1004
1004
|
return this.options.healthy;
|
|
1005
1005
|
}
|
|
1006
|
-
async complete(
|
|
1006
|
+
async complete(request) {
|
|
1007
1007
|
this._completeCount++;
|
|
1008
1008
|
if (this.options.shouldThrow) {
|
|
1009
1009
|
throw new Error(this.options.errorMessage);
|
|
1010
1010
|
}
|
|
1011
|
-
const lastMessage =
|
|
1011
|
+
const lastMessage = request.messages[request.messages.length - 1];
|
|
1012
1012
|
const inputKey = lastMessage ? `${lastMessage.role}:${lastMessage.content}` : "empty";
|
|
1013
1013
|
const hash = fnv1a32(inputKey);
|
|
1014
1014
|
const deterministicContent = `fake-response-${hash.toString(16).padStart(8, "0")}`;
|
|
1015
1015
|
const usage = {
|
|
1016
|
-
prompt_tokens:
|
|
1016
|
+
prompt_tokens: request.messages.length * 10,
|
|
1017
1017
|
completion_tokens: 20,
|
|
1018
|
-
total_tokens:
|
|
1018
|
+
total_tokens: request.messages.length * 10 + 20
|
|
1019
1019
|
};
|
|
1020
1020
|
if (this.options.toolCallResponses.length > 0) {
|
|
1021
1021
|
return {
|
|
@@ -11711,13 +11711,13 @@ Validation: ${validation.pass ? "PASS" : "NEEDS IMPROVEMENT"} (${validation.over
|
|
|
11711
11711
|
// -------------------------------------------------------------------------
|
|
11712
11712
|
// Phase 1: Seed Analysis
|
|
11713
11713
|
// -------------------------------------------------------------------------
|
|
11714
|
-
async analyzeSeed(
|
|
11714
|
+
async analyzeSeed(request) {
|
|
11715
11715
|
const prompt = loadBuilderPrompt("seed-analysis.md", {
|
|
11716
|
-
skill_request:
|
|
11716
|
+
skill_request: request
|
|
11717
11717
|
});
|
|
11718
11718
|
const response = await this.llmCall([
|
|
11719
11719
|
{ role: "system", content: prompt },
|
|
11720
|
-
{ role: "user", content: `Analyze this skill request: "${
|
|
11720
|
+
{ role: "user", content: `Analyze this skill request: "${request}"` }
|
|
11721
11721
|
]);
|
|
11722
11722
|
const jsonStr = extractJSON(response);
|
|
11723
11723
|
const seed = JSON.parse(jsonStr);
|
|
@@ -24287,22 +24287,22 @@ var init_snippetPacker = __esm({
|
|
|
24287
24287
|
});
|
|
24288
24288
|
|
|
24289
24289
|
// packages/retrieval/dist/contextAssembler.js
|
|
24290
|
-
async function assembleContext(
|
|
24290
|
+
async function assembleContext(request, opts) {
|
|
24291
24291
|
const { repoRoot, graph, semanticEngine, summaryMap, maxFiles = 8, maxSnippets = 20, maxLogs = 3, maxTokens = 24e3, expandNeighbors = true, architectureNote, priorAttemptNote } = opts;
|
|
24292
24292
|
const lexOpts = {
|
|
24293
24293
|
rootDir: repoRoot,
|
|
24294
24294
|
includePatterns: [],
|
|
24295
|
-
includeTests:
|
|
24296
|
-
includeConfigs:
|
|
24295
|
+
includeTests: request.includeTests,
|
|
24296
|
+
includeConfigs: request.includeConfigs,
|
|
24297
24297
|
maxMatches: 30
|
|
24298
24298
|
};
|
|
24299
24299
|
const [pathMatches, symbolMatches, errorMatches, semanticResults] = await Promise.all([
|
|
24300
|
-
Promise.all(
|
|
24301
|
-
Promise.all(
|
|
24302
|
-
Promise.all(
|
|
24303
|
-
semanticEngine?.isAvailable ? semanticEngine.search(
|
|
24300
|
+
Promise.all(request.pathsHint.map((p) => searchByPath(p, { rootDir: repoRoot, maxMatches: 10 }))).then((res) => res.flat()),
|
|
24301
|
+
Promise.all(request.symbolHint.map((s) => searchBySymbol(s, { rootDir: repoRoot, maxMatches: 10 }))).then((res) => res.flat()),
|
|
24302
|
+
Promise.all(request.errorHint.slice(0, maxLogs).map((e) => searchByError(e, { rootDir: repoRoot, maxMatches: 5 }))).then((res) => res.flat()),
|
|
24303
|
+
semanticEngine?.isAvailable ? semanticEngine.search(request.query, maxFiles) : Promise.resolve([])
|
|
24304
24304
|
]);
|
|
24305
|
-
const queryType = classifyQuery(
|
|
24305
|
+
const queryType = classifyQuery(request.query);
|
|
24306
24306
|
const rrfK = adaptiveK(queryType, opts.rrfConfig?.k);
|
|
24307
24307
|
const wFts = opts.rrfConfig?.weightFts ?? 1;
|
|
24308
24308
|
const wSem = opts.rrfConfig?.weightSemantic ?? 1;
|
|
@@ -24373,7 +24373,7 @@ async function assembleContext(request2, opts) {
|
|
|
24373
24373
|
}
|
|
24374
24374
|
}
|
|
24375
24375
|
return {
|
|
24376
|
-
request
|
|
24376
|
+
request,
|
|
24377
24377
|
files,
|
|
24378
24378
|
snippets: packed.slice(0, maxSnippets),
|
|
24379
24379
|
architectureNote,
|
|
@@ -28232,14 +28232,14 @@ ${transcript}`
|
|
|
28232
28232
|
* assembles and returns the same response format as chatCompletion().
|
|
28233
28233
|
* The non-streaming chatCompletion path is NEVER touched by this code.
|
|
28234
28234
|
*/
|
|
28235
|
-
async streamingRequest(
|
|
28235
|
+
async streamingRequest(request, turn) {
|
|
28236
28236
|
const backend = this.backend;
|
|
28237
28237
|
let content = "";
|
|
28238
28238
|
let inThinkTag = false;
|
|
28239
28239
|
const toolCallAccumulators = /* @__PURE__ */ new Map();
|
|
28240
28240
|
let streamUsage;
|
|
28241
28241
|
this.emit({ type: "stream_start", turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
28242
|
-
for await (const chunk of backend.chatCompletionStream(
|
|
28242
|
+
for await (const chunk of backend.chatCompletionStream(request)) {
|
|
28243
28243
|
if (this.aborted)
|
|
28244
28244
|
break;
|
|
28245
28245
|
if (chunk.type === "usage" && chunk.usage) {
|
|
@@ -28266,6 +28266,19 @@ ${transcript}`
|
|
|
28266
28266
|
}
|
|
28267
28267
|
if (kind === "content") {
|
|
28268
28268
|
content += fragment;
|
|
28269
|
+
if (content.length > 400 && content.length % 200 < fragment.length) {
|
|
28270
|
+
const half = Math.floor(content.length / 2);
|
|
28271
|
+
const firstHalf = content.slice(half - 150, half);
|
|
28272
|
+
const secondHalf = content.slice(-150);
|
|
28273
|
+
if (firstHalf.length >= 100 && firstHalf === secondHalf) {
|
|
28274
|
+
this.emit({
|
|
28275
|
+
type: "status",
|
|
28276
|
+
content: "Aborting generation \u2014 intra-response repetition detected (model stuck in text loop)",
|
|
28277
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
28278
|
+
});
|
|
28279
|
+
break;
|
|
28280
|
+
}
|
|
28281
|
+
}
|
|
28269
28282
|
}
|
|
28270
28283
|
this.emit({
|
|
28271
28284
|
type: "stream_token",
|
|
@@ -28349,13 +28362,13 @@ ${transcript}`
|
|
|
28349
28362
|
}
|
|
28350
28363
|
return headers;
|
|
28351
28364
|
}
|
|
28352
|
-
async chatCompletion(
|
|
28365
|
+
async chatCompletion(request) {
|
|
28353
28366
|
const body = {
|
|
28354
28367
|
model: this.model,
|
|
28355
|
-
messages:
|
|
28356
|
-
tools:
|
|
28357
|
-
temperature:
|
|
28358
|
-
max_tokens:
|
|
28368
|
+
messages: request.messages,
|
|
28369
|
+
tools: request.tools,
|
|
28370
|
+
temperature: request.temperature,
|
|
28371
|
+
max_tokens: request.maxTokens,
|
|
28359
28372
|
think: this.thinking
|
|
28360
28373
|
};
|
|
28361
28374
|
const fetchOpts = {
|
|
@@ -28412,13 +28425,13 @@ ${transcript}`
|
|
|
28412
28425
|
* Uses `stream: true` and the current thinking setting.
|
|
28413
28426
|
* The existing chatCompletion() method is completely unmodified.
|
|
28414
28427
|
*/
|
|
28415
|
-
async *chatCompletionStream(
|
|
28428
|
+
async *chatCompletionStream(request) {
|
|
28416
28429
|
const body = {
|
|
28417
28430
|
model: this.model,
|
|
28418
|
-
messages:
|
|
28419
|
-
tools:
|
|
28420
|
-
temperature:
|
|
28421
|
-
max_tokens:
|
|
28431
|
+
messages: request.messages,
|
|
28432
|
+
tools: request.tools,
|
|
28433
|
+
temperature: request.temperature,
|
|
28434
|
+
max_tokens: request.maxTokens,
|
|
28422
28435
|
stream: true,
|
|
28423
28436
|
stream_options: { include_usage: true },
|
|
28424
28437
|
think: this.thinking
|
|
@@ -28459,8 +28472,9 @@ ${transcript}`
|
|
|
28459
28472
|
continue;
|
|
28460
28473
|
const delta = choice.delta;
|
|
28461
28474
|
const finishReason = choice.finish_reason;
|
|
28462
|
-
|
|
28463
|
-
|
|
28475
|
+
const reasoningToken = delta?.reasoning ?? delta?.reasoning_content;
|
|
28476
|
+
if (reasoningToken) {
|
|
28477
|
+
yield { type: "content", content: reasoningToken, thinking: true };
|
|
28464
28478
|
}
|
|
28465
28479
|
if (delta?.content) {
|
|
28466
28480
|
yield { type: "content", content: delta.content };
|
|
@@ -28538,7 +28552,7 @@ var init_nexusBackend = __esm({
|
|
|
28538
28552
|
this.targetPeer = peerId;
|
|
28539
28553
|
this.consecutiveFailures = 0;
|
|
28540
28554
|
}
|
|
28541
|
-
async chatCompletion(
|
|
28555
|
+
async chatCompletion(request) {
|
|
28542
28556
|
if (this.consecutiveFailures >= _NexusAgenticBackend.MAX_CONSECUTIVE_FAILURES) {
|
|
28543
28557
|
const err = new Error(`Remote peer unreachable after ${this.consecutiveFailures} attempts. Use /endpoint to switch to a local model or reconnect.`);
|
|
28544
28558
|
err.fatal = true;
|
|
@@ -28546,10 +28560,10 @@ var init_nexusBackend = __esm({
|
|
|
28546
28560
|
}
|
|
28547
28561
|
const daemonArgs = {
|
|
28548
28562
|
model: this.model,
|
|
28549
|
-
messages: JSON.stringify(
|
|
28550
|
-
tools: JSON.stringify(
|
|
28551
|
-
temperature: String(
|
|
28552
|
-
max_tokens: String(
|
|
28563
|
+
messages: JSON.stringify(request.messages),
|
|
28564
|
+
tools: JSON.stringify(request.tools),
|
|
28565
|
+
temperature: String(request.temperature),
|
|
28566
|
+
max_tokens: String(request.maxTokens)
|
|
28553
28567
|
};
|
|
28554
28568
|
if (this.targetPeer) {
|
|
28555
28569
|
daemonArgs.target_peer = this.targetPeer;
|
|
@@ -28560,7 +28574,7 @@ var init_nexusBackend = __esm({
|
|
|
28560
28574
|
daemonArgs.think = String(this.thinking);
|
|
28561
28575
|
let rawResult;
|
|
28562
28576
|
try {
|
|
28563
|
-
rawResult = await this.sendFn("remote_infer", daemonArgs,
|
|
28577
|
+
rawResult = await this.sendFn("remote_infer", daemonArgs, request.timeoutMs || 12e4);
|
|
28564
28578
|
} catch (sendErr) {
|
|
28565
28579
|
this.consecutiveFailures++;
|
|
28566
28580
|
throw sendErr;
|
|
@@ -28653,15 +28667,15 @@ var init_nexusBackend = __esm({
|
|
|
28653
28667
|
* the file for token-by-token delivery from the remote peer.
|
|
28654
28668
|
* Falls back to unary + word-split if streaming setup fails.
|
|
28655
28669
|
*/
|
|
28656
|
-
async *chatCompletionStream(
|
|
28670
|
+
async *chatCompletionStream(request) {
|
|
28657
28671
|
const streamFile = join47(tmpdir7(), `nexus-stream-${randomBytes11(6).toString("hex")}.jsonl`);
|
|
28658
28672
|
writeFileSync11(streamFile, "", "utf8");
|
|
28659
28673
|
const daemonArgs = {
|
|
28660
28674
|
model: this.model,
|
|
28661
|
-
messages: JSON.stringify(
|
|
28662
|
-
tools: JSON.stringify(
|
|
28663
|
-
temperature: String(
|
|
28664
|
-
max_tokens: String(
|
|
28675
|
+
messages: JSON.stringify(request.messages),
|
|
28676
|
+
tools: JSON.stringify(request.tools),
|
|
28677
|
+
temperature: String(request.temperature),
|
|
28678
|
+
max_tokens: String(request.maxTokens),
|
|
28665
28679
|
stream_file: streamFile
|
|
28666
28680
|
};
|
|
28667
28681
|
if (this.targetPeer)
|
|
@@ -28671,7 +28685,7 @@ var init_nexusBackend = __esm({
|
|
|
28671
28685
|
daemonArgs.think = String(this.thinking);
|
|
28672
28686
|
let rawResult;
|
|
28673
28687
|
try {
|
|
28674
|
-
rawResult = await this.sendFn("remote_infer", daemonArgs,
|
|
28688
|
+
rawResult = await this.sendFn("remote_infer", daemonArgs, request.timeoutMs || 12e4);
|
|
28675
28689
|
} catch (sendErr) {
|
|
28676
28690
|
this.consecutiveFailures++;
|
|
28677
28691
|
try {
|
|
@@ -28744,7 +28758,7 @@ var init_nexusBackend = __esm({
|
|
|
28744
28758
|
this.consecutiveFailures = 0;
|
|
28745
28759
|
let position = 0;
|
|
28746
28760
|
let done = false;
|
|
28747
|
-
const deadline = Date.now() + (
|
|
28761
|
+
const deadline = Date.now() + (request.timeoutMs ?? 12e4);
|
|
28748
28762
|
try {
|
|
28749
28763
|
while (!done && Date.now() < deadline) {
|
|
28750
28764
|
await new Promise((resolve36) => {
|
|
@@ -28917,10 +28931,10 @@ var init_cascadeBackend = __esm({
|
|
|
28917
28931
|
get fallbackCount() {
|
|
28918
28932
|
return this.endpoints.filter((ep, i) => i !== this.activeIndex && ep.modelAvailable !== false).length;
|
|
28919
28933
|
}
|
|
28920
|
-
async chatCompletion(
|
|
28934
|
+
async chatCompletion(request) {
|
|
28921
28935
|
const backend = this.getActiveBackend();
|
|
28922
28936
|
try {
|
|
28923
|
-
const result = await backend.chatCompletion(
|
|
28937
|
+
const result = await backend.chatCompletion(request);
|
|
28924
28938
|
this.consecutiveFailures = 0;
|
|
28925
28939
|
return result;
|
|
28926
28940
|
} catch (err) {
|
|
@@ -28936,7 +28950,7 @@ var init_cascadeBackend = __esm({
|
|
|
28936
28950
|
this.onSwitch?.(from, to, reason);
|
|
28937
28951
|
const newBackend = this.getActiveBackend();
|
|
28938
28952
|
try {
|
|
28939
|
-
const result = await newBackend.chatCompletion(
|
|
28953
|
+
const result = await newBackend.chatCompletion(request);
|
|
28940
28954
|
return result;
|
|
28941
28955
|
} catch (cascadeErr) {
|
|
28942
28956
|
this.consecutiveFailures++;
|
|
@@ -28950,11 +28964,11 @@ var init_cascadeBackend = __esm({
|
|
|
28950
28964
|
/**
|
|
28951
28965
|
* Streaming support — delegates to active backend's stream method if available.
|
|
28952
28966
|
*/
|
|
28953
|
-
async *chatCompletionStream(
|
|
28967
|
+
async *chatCompletionStream(request) {
|
|
28954
28968
|
const backend = this.getActiveBackend();
|
|
28955
28969
|
const streamable = backend;
|
|
28956
28970
|
if (typeof streamable.chatCompletionStream !== "function") {
|
|
28957
|
-
const result = await this.chatCompletion(
|
|
28971
|
+
const result = await this.chatCompletion(request);
|
|
28958
28972
|
const choice = result.choices[0];
|
|
28959
28973
|
if (choice?.message.content) {
|
|
28960
28974
|
yield { type: "content", content: choice.message.content };
|
|
@@ -28985,7 +28999,7 @@ var init_cascadeBackend = __esm({
|
|
|
28985
28999
|
return;
|
|
28986
29000
|
}
|
|
28987
29001
|
try {
|
|
28988
|
-
for await (const chunk of streamable.chatCompletionStream(
|
|
29002
|
+
for await (const chunk of streamable.chatCompletionStream(request)) {
|
|
28989
29003
|
yield chunk;
|
|
28990
29004
|
}
|
|
28991
29005
|
this.consecutiveFailures = 0;
|
|
@@ -28999,7 +29013,7 @@ var init_cascadeBackend = __esm({
|
|
|
28999
29013
|
this.activeIndex = nextIdx;
|
|
29000
29014
|
this.consecutiveFailures = 0;
|
|
29001
29015
|
this.onSwitch?.(from, to, err instanceof Error ? err.message : String(err));
|
|
29002
|
-
const result = await this.chatCompletion(
|
|
29016
|
+
const result = await this.chatCompletion(request);
|
|
29003
29017
|
const choice = result.choices[0];
|
|
29004
29018
|
if (choice?.message.content) {
|
|
29005
29019
|
yield { type: "content", content: choice.message.content };
|
|
@@ -32596,7 +32610,7 @@ var require_websocket = __commonJS({
|
|
|
32596
32610
|
"node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js"(exports, module) {
|
|
32597
32611
|
"use strict";
|
|
32598
32612
|
var EventEmitter7 = __require("events");
|
|
32599
|
-
var
|
|
32613
|
+
var https2 = __require("https");
|
|
32600
32614
|
var http2 = __require("http");
|
|
32601
32615
|
var net = __require("net");
|
|
32602
32616
|
var tls = __require("tls");
|
|
@@ -33131,7 +33145,7 @@ var require_websocket = __commonJS({
|
|
|
33131
33145
|
}
|
|
33132
33146
|
const defaultPort = isSecure ? 443 : 80;
|
|
33133
33147
|
const key = randomBytes18(16).toString("base64");
|
|
33134
|
-
const
|
|
33148
|
+
const request = isSecure ? https2.request : http2.request;
|
|
33135
33149
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
33136
33150
|
let perMessageDeflate;
|
|
33137
33151
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -33208,12 +33222,12 @@ var require_websocket = __commonJS({
|
|
|
33208
33222
|
if (opts.auth && !options.headers.authorization) {
|
|
33209
33223
|
options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
|
|
33210
33224
|
}
|
|
33211
|
-
req = websocket._req =
|
|
33225
|
+
req = websocket._req = request(opts);
|
|
33212
33226
|
if (websocket._redirects) {
|
|
33213
33227
|
websocket.emit("redirect", websocket.url, req);
|
|
33214
33228
|
}
|
|
33215
33229
|
} else {
|
|
33216
|
-
req = websocket._req =
|
|
33230
|
+
req = websocket._req = request(opts);
|
|
33217
33231
|
}
|
|
33218
33232
|
if (opts.timeout) {
|
|
33219
33233
|
req.on("timeout", () => {
|
|
@@ -37601,7 +37615,7 @@ var init_peer_mesh = __esm({
|
|
|
37601
37615
|
* Send an inference request to a specific peer.
|
|
37602
37616
|
* Returns a promise that resolves when the response arrives.
|
|
37603
37617
|
*/
|
|
37604
|
-
async requestInference(peerId,
|
|
37618
|
+
async requestInference(peerId, request, timeoutMs = 12e4) {
|
|
37605
37619
|
const ws = this.connections.get(peerId);
|
|
37606
37620
|
if (!ws || ws.readyState !== import_websocket.default.OPEN) {
|
|
37607
37621
|
throw new Error(`Peer ${peerId} not connected`);
|
|
@@ -37613,7 +37627,7 @@ var init_peer_mesh = __esm({
|
|
|
37613
37627
|
reject(new Error(`Inference timeout (${timeoutMs}ms)`));
|
|
37614
37628
|
}, timeoutMs);
|
|
37615
37629
|
this.pendingRequests.set(msgId, { resolve: resolve36, reject, timeout, chunks: [] });
|
|
37616
|
-
this.sendMsg(ws, "infer_request",
|
|
37630
|
+
this.sendMsg(ws, "infer_request", request, msgId);
|
|
37617
37631
|
});
|
|
37618
37632
|
}
|
|
37619
37633
|
// ── Inbound connection handling ─────────────────────────────────────────
|
|
@@ -37955,7 +37969,7 @@ var init_inference_router = __esm({
|
|
|
37955
37969
|
}
|
|
37956
37970
|
}));
|
|
37957
37971
|
}
|
|
37958
|
-
const
|
|
37972
|
+
const request = {
|
|
37959
37973
|
model,
|
|
37960
37974
|
messages: redactedMessages,
|
|
37961
37975
|
tools: redactedTools,
|
|
@@ -37968,7 +37982,7 @@ var init_inference_router = __esm({
|
|
|
37968
37982
|
if (redactedCount > 0)
|
|
37969
37983
|
this._totalRedacted++;
|
|
37970
37984
|
try {
|
|
37971
|
-
const response = await this.mesh.requestInference(peer.peerId,
|
|
37985
|
+
const response = await this.mesh.requestInference(peer.peerId, request, options?.timeoutMs ?? this.defaultTimeoutMs);
|
|
37972
37986
|
const injectedContent = this.vault.inject(response.content);
|
|
37973
37987
|
const injectedToolCalls = response.toolCalls?.map((tc) => ({
|
|
37974
37988
|
...tc,
|
|
@@ -38001,8 +38015,8 @@ var init_inference_router = __esm({
|
|
|
38001
38015
|
* The response text may contain placeholders from the requester's vault,
|
|
38002
38016
|
* which is fine — we don't have their secrets and don't need them.
|
|
38003
38017
|
*/
|
|
38004
|
-
async handleInboundRequest(
|
|
38005
|
-
for (const msg of
|
|
38018
|
+
async handleInboundRequest(request, inferFn) {
|
|
38019
|
+
for (const msg of request.messages) {
|
|
38006
38020
|
const found = this.vault.scan(msg.content);
|
|
38007
38021
|
if (found.length > 0) {
|
|
38008
38022
|
this.emit("leaked_secrets_detected", {
|
|
@@ -38011,7 +38025,7 @@ var init_inference_router = __esm({
|
|
|
38011
38025
|
});
|
|
38012
38026
|
}
|
|
38013
38027
|
}
|
|
38014
|
-
return inferFn(
|
|
38028
|
+
return inferFn(request);
|
|
38015
38029
|
}
|
|
38016
38030
|
// ── Scoring ────────────────────────────────────────────────────────────
|
|
38017
38031
|
scoreRoute(peer, model) {
|
|
@@ -53593,6 +53607,7 @@ var init_stream_renderer = __esm({
|
|
|
53593
53607
|
process.stdout.write(`\x1B[1A\x1B[2K${dimText(" \u23BF ")}${dimItalic(`thought for ${this.thinkingTokenCount} tokens`)}
|
|
53594
53608
|
`);
|
|
53595
53609
|
this.thinkingTokenCount = 0;
|
|
53610
|
+
this.lineStarted = false;
|
|
53596
53611
|
}
|
|
53597
53612
|
if (kind === "tool_args" && !this.inToolArgs) {
|
|
53598
53613
|
this.flushPartial(kind);
|
|
@@ -53645,8 +53660,6 @@ var init_stream_renderer = __esm({
|
|
|
53645
53660
|
if (this.lineStarted) {
|
|
53646
53661
|
process.stdout.write("\n");
|
|
53647
53662
|
this.lineStarted = false;
|
|
53648
|
-
} else {
|
|
53649
|
-
process.stdout.write("\n");
|
|
53650
53663
|
}
|
|
53651
53664
|
return;
|
|
53652
53665
|
}
|
|
@@ -61892,6 +61905,7 @@ __export(serve_exports, {
|
|
|
61892
61905
|
startApiServer: () => startApiServer
|
|
61893
61906
|
});
|
|
61894
61907
|
import * as http from "node:http";
|
|
61908
|
+
import * as https from "node:https";
|
|
61895
61909
|
import { createRequire as createRequire2 } from "node:module";
|
|
61896
61910
|
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
61897
61911
|
import { dirname as dirname20, join as join69, resolve as resolve31 } from "node:path";
|
|
@@ -61977,20 +61991,29 @@ async function parseJsonBody(req) {
|
|
|
61977
61991
|
return null;
|
|
61978
61992
|
}
|
|
61979
61993
|
}
|
|
61994
|
+
function backendAuthHeaders() {
|
|
61995
|
+
const config = loadConfig();
|
|
61996
|
+
if (config.apiKey)
|
|
61997
|
+
return { Authorization: `Bearer ${config.apiKey}` };
|
|
61998
|
+
return {};
|
|
61999
|
+
}
|
|
61980
62000
|
function ollamaRequest(ollamaUrl, path, method, body) {
|
|
61981
62001
|
return new Promise((resolve36, reject) => {
|
|
61982
62002
|
const url = new URL(path, ollamaUrl);
|
|
62003
|
+
const isHttps = url.protocol === "https:";
|
|
61983
62004
|
const options = {
|
|
61984
62005
|
hostname: url.hostname,
|
|
61985
|
-
port: url.port,
|
|
62006
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
61986
62007
|
path: url.pathname + url.search,
|
|
61987
62008
|
method,
|
|
61988
62009
|
headers: {
|
|
61989
62010
|
"Content-Type": "application/json",
|
|
61990
|
-
...body ? { "Content-Length": Buffer.byteLength(body) } : {}
|
|
62011
|
+
...body ? { "Content-Length": Buffer.byteLength(body) } : {},
|
|
62012
|
+
...backendAuthHeaders()
|
|
61991
62013
|
}
|
|
61992
62014
|
};
|
|
61993
|
-
const
|
|
62015
|
+
const transport = isHttps ? https : http;
|
|
62016
|
+
const proxyReq = transport.request(options, (proxyRes) => {
|
|
61994
62017
|
const chunks = [];
|
|
61995
62018
|
proxyRes.on("data", (chunk) => chunks.push(chunk));
|
|
61996
62019
|
proxyRes.on("end", () => {
|
|
@@ -62012,17 +62035,20 @@ function ollamaRequest(ollamaUrl, path, method, body) {
|
|
|
62012
62035
|
}
|
|
62013
62036
|
function ollamaStream(ollamaUrl, path, method, body, onData, onEnd, onError) {
|
|
62014
62037
|
const url = new URL(path, ollamaUrl);
|
|
62038
|
+
const isHttps = url.protocol === "https:";
|
|
62015
62039
|
const options = {
|
|
62016
62040
|
hostname: url.hostname,
|
|
62017
|
-
port: url.port,
|
|
62041
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
62018
62042
|
path: url.pathname + url.search,
|
|
62019
62043
|
method,
|
|
62020
62044
|
headers: {
|
|
62021
62045
|
"Content-Type": "application/json",
|
|
62022
|
-
...body ? { "Content-Length": Buffer.byteLength(body) } : {}
|
|
62046
|
+
...body ? { "Content-Length": Buffer.byteLength(body) } : {},
|
|
62047
|
+
...backendAuthHeaders()
|
|
62023
62048
|
}
|
|
62024
62049
|
};
|
|
62025
|
-
const
|
|
62050
|
+
const transport = isHttps ? https : http;
|
|
62051
|
+
const proxyReq = transport.request(options, (proxyRes) => {
|
|
62026
62052
|
proxyRes.setEncoding("utf-8");
|
|
62027
62053
|
proxyRes.on("data", onData);
|
|
62028
62054
|
proxyRes.on("end", onEnd);
|
|
@@ -62117,14 +62143,17 @@ function handleHealth(res) {
|
|
|
62117
62143
|
}
|
|
62118
62144
|
async function handleHealthReady(res, ollamaUrl) {
|
|
62119
62145
|
try {
|
|
62120
|
-
const
|
|
62146
|
+
const config = loadConfig();
|
|
62147
|
+
const isVllm = config.backendType === "vllm";
|
|
62148
|
+
const checkPath = isVllm ? "/v1/models" : "/api/tags";
|
|
62149
|
+
const result = await ollamaRequest(ollamaUrl, checkPath, "GET");
|
|
62121
62150
|
if (result.status === 200) {
|
|
62122
|
-
jsonResponse(res, 200, { status: "ready",
|
|
62151
|
+
jsonResponse(res, 200, { status: "ready", backend: "reachable", type: config.backendType || "ollama" });
|
|
62123
62152
|
} else {
|
|
62124
|
-
jsonResponse(res, 503, { status: "not_ready",
|
|
62153
|
+
jsonResponse(res, 503, { status: "not_ready", backend: "unreachable", code: result.status });
|
|
62125
62154
|
}
|
|
62126
62155
|
} catch {
|
|
62127
|
-
jsonResponse(res, 503, { status: "not_ready",
|
|
62156
|
+
jsonResponse(res, 503, { status: "not_ready", backend: "unreachable" });
|
|
62128
62157
|
}
|
|
62129
62158
|
}
|
|
62130
62159
|
function handleHealthStartup(res) {
|
|
@@ -62143,9 +62172,20 @@ function handleMetrics(res) {
|
|
|
62143
62172
|
}
|
|
62144
62173
|
async function handleV1Models(res, ollamaUrl) {
|
|
62145
62174
|
try {
|
|
62175
|
+
const config = loadConfig();
|
|
62176
|
+
const isVllm = config.backendType === "vllm";
|
|
62177
|
+
if (isVllm) {
|
|
62178
|
+
const result2 = await ollamaRequest(ollamaUrl, "/v1/models", "GET");
|
|
62179
|
+
if (result2.status !== 200) {
|
|
62180
|
+
jsonResponse(res, result2.status, { error: "Failed to fetch models from backend" });
|
|
62181
|
+
return;
|
|
62182
|
+
}
|
|
62183
|
+
jsonResponse(res, 200, JSON.parse(result2.body));
|
|
62184
|
+
return;
|
|
62185
|
+
}
|
|
62146
62186
|
const result = await ollamaRequest(ollamaUrl, "/api/tags", "GET");
|
|
62147
62187
|
if (result.status !== 200) {
|
|
62148
|
-
jsonResponse(res, result.status, { error: "Failed to fetch models from
|
|
62188
|
+
jsonResponse(res, result.status, { error: "Failed to fetch models from backend" });
|
|
62149
62189
|
return;
|
|
62150
62190
|
}
|
|
62151
62191
|
const ollamaBody = JSON.parse(result.body);
|
|
@@ -62158,7 +62198,7 @@ async function handleV1Models(res, ollamaUrl) {
|
|
|
62158
62198
|
jsonResponse(res, 200, { object: "list", data: models });
|
|
62159
62199
|
} catch (err) {
|
|
62160
62200
|
jsonResponse(res, 502, {
|
|
62161
|
-
error: "Failed to proxy to
|
|
62201
|
+
error: "Failed to proxy to backend",
|
|
62162
62202
|
message: err instanceof Error ? err.message : String(err)
|
|
62163
62203
|
});
|
|
62164
62204
|
}
|
|
@@ -62172,6 +62212,51 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
|
|
|
62172
62212
|
const requestBody = body;
|
|
62173
62213
|
const stream = requestBody["stream"] === true;
|
|
62174
62214
|
const model = requestBody["model"] || "unknown";
|
|
62215
|
+
const config = loadConfig();
|
|
62216
|
+
const isVllm = config.backendType === "vllm";
|
|
62217
|
+
if (isVllm) {
|
|
62218
|
+
const payload = JSON.stringify(requestBody);
|
|
62219
|
+
if (stream) {
|
|
62220
|
+
res.writeHead(200, {
|
|
62221
|
+
"Content-Type": "text/event-stream",
|
|
62222
|
+
"Cache-Control": "no-cache",
|
|
62223
|
+
"Connection": "keep-alive",
|
|
62224
|
+
"Access-Control-Allow-Origin": "*",
|
|
62225
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
62226
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
62227
|
+
});
|
|
62228
|
+
ollamaStream(ollamaUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
|
|
62229
|
+
res.write(chunk);
|
|
62230
|
+
}, () => {
|
|
62231
|
+
res.end();
|
|
62232
|
+
}, (err) => {
|
|
62233
|
+
try {
|
|
62234
|
+
res.write(`data: ${JSON.stringify({ error: err.message })}
|
|
62235
|
+
|
|
62236
|
+
`);
|
|
62237
|
+
} catch {
|
|
62238
|
+
}
|
|
62239
|
+
res.end();
|
|
62240
|
+
});
|
|
62241
|
+
} else {
|
|
62242
|
+
try {
|
|
62243
|
+
const result = await ollamaRequest(ollamaUrl, "/v1/chat/completions", "POST", payload);
|
|
62244
|
+
if (result.status !== 200) {
|
|
62245
|
+
jsonResponse(res, result.status, { error: "Backend request failed", details: result.body });
|
|
62246
|
+
return;
|
|
62247
|
+
}
|
|
62248
|
+
const parsed = JSON.parse(result.body);
|
|
62249
|
+
if (parsed.usage) {
|
|
62250
|
+
metrics.totalTokensIn += parsed.usage.prompt_tokens ?? 0;
|
|
62251
|
+
metrics.totalTokensOut += parsed.usage.completion_tokens ?? 0;
|
|
62252
|
+
}
|
|
62253
|
+
jsonResponse(res, 200, parsed);
|
|
62254
|
+
} catch (err) {
|
|
62255
|
+
jsonResponse(res, 502, { error: "Failed to proxy to backend", message: err instanceof Error ? err.message : String(err) });
|
|
62256
|
+
}
|
|
62257
|
+
}
|
|
62258
|
+
return;
|
|
62259
|
+
}
|
|
62175
62260
|
const ollamaPayload = JSON.stringify({
|
|
62176
62261
|
...requestBody,
|
|
62177
62262
|
stream,
|
|
@@ -62329,6 +62414,22 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
|
|
|
62329
62414
|
return;
|
|
62330
62415
|
}
|
|
62331
62416
|
const requestBody = body;
|
|
62417
|
+
const config = loadConfig();
|
|
62418
|
+
const isVllm = config.backendType === "vllm";
|
|
62419
|
+
if (isVllm) {
|
|
62420
|
+
try {
|
|
62421
|
+
const payload = JSON.stringify(requestBody);
|
|
62422
|
+
const result = await ollamaRequest(ollamaUrl, "/v1/embeddings", "POST", payload);
|
|
62423
|
+
if (result.status !== 200) {
|
|
62424
|
+
jsonResponse(res, result.status, { error: "Backend embeddings request failed", details: result.body });
|
|
62425
|
+
return;
|
|
62426
|
+
}
|
|
62427
|
+
jsonResponse(res, 200, JSON.parse(result.body));
|
|
62428
|
+
} catch (err) {
|
|
62429
|
+
jsonResponse(res, 502, { error: "Failed to proxy to backend", message: err instanceof Error ? err.message : String(err) });
|
|
62430
|
+
}
|
|
62431
|
+
return;
|
|
62432
|
+
}
|
|
62332
62433
|
const ollamaPayload = JSON.stringify({
|
|
62333
62434
|
model: requestBody["model"],
|
|
62334
62435
|
input: requestBody["input"]
|
|
@@ -62337,7 +62438,7 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
|
|
|
62337
62438
|
const result = await ollamaRequest(ollamaUrl, "/api/embed", "POST", ollamaPayload);
|
|
62338
62439
|
if (result.status !== 200) {
|
|
62339
62440
|
jsonResponse(res, result.status, {
|
|
62340
|
-
error: "
|
|
62441
|
+
error: "Backend embeddings request failed",
|
|
62341
62442
|
details: result.body
|
|
62342
62443
|
});
|
|
62343
62444
|
return;
|
|
@@ -62356,7 +62457,7 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
|
|
|
62356
62457
|
});
|
|
62357
62458
|
} catch (err) {
|
|
62358
62459
|
jsonResponse(res, 502, {
|
|
62359
|
-
error: "Failed to proxy to
|
|
62460
|
+
error: "Failed to proxy to backend",
|
|
62360
62461
|
message: err instanceof Error ? err.message : String(err)
|
|
62361
62462
|
});
|
|
62362
62463
|
}
|
|
@@ -62564,10 +62665,16 @@ function handleV1RunsDelete(res, id) {
|
|
|
62564
62665
|
}
|
|
62565
62666
|
jsonResponse(res, 200, { run_id: id, status: "aborted" });
|
|
62566
62667
|
}
|
|
62668
|
+
function redactSecrets(obj) {
|
|
62669
|
+
const copy = { ...obj };
|
|
62670
|
+
if (typeof copy.apiKey === "string" && copy.apiKey)
|
|
62671
|
+
copy.apiKey = "[redacted]";
|
|
62672
|
+
return copy;
|
|
62673
|
+
}
|
|
62567
62674
|
function handleGetConfig(res) {
|
|
62568
62675
|
const config = loadConfig();
|
|
62569
62676
|
const settings = loadGlobalSettings();
|
|
62570
|
-
jsonResponse(res, 200, { config, settings });
|
|
62677
|
+
jsonResponse(res, 200, { config: redactSecrets(config), settings: redactSecrets(settings) });
|
|
62571
62678
|
}
|
|
62572
62679
|
async function handlePatchConfig(req, res) {
|
|
62573
62680
|
const body = await parseJsonBody(req);
|
|
@@ -62595,7 +62702,7 @@ async function handlePatchConfig(req, res) {
|
|
|
62595
62702
|
settingsUpdate.timeoutMs = updates["timeoutMs"];
|
|
62596
62703
|
saveGlobalSettings(settingsUpdate);
|
|
62597
62704
|
const updatedConfig = loadConfig();
|
|
62598
|
-
jsonResponse(res, 200, { config: updatedConfig, updated: Object.keys(settingsUpdate) });
|
|
62705
|
+
jsonResponse(res, 200, { config: redactSecrets(updatedConfig), updated: Object.keys(settingsUpdate) });
|
|
62599
62706
|
}
|
|
62600
62707
|
function handleGetConfigModel(res) {
|
|
62601
62708
|
const config = loadConfig();
|
|
@@ -62936,7 +63043,8 @@ function startApiServer(options = {}) {
|
|
|
62936
63043
|
`);
|
|
62937
63044
|
process.stderr.write(` Listening on http://${host}:${port}
|
|
62938
63045
|
`);
|
|
62939
|
-
|
|
63046
|
+
const beType = config.backendType || "ollama";
|
|
63047
|
+
process.stderr.write(` Backend (${beType}): ${ollamaUrl}
|
|
62940
63048
|
`);
|
|
62941
63049
|
if (process.env["OA_API_KEYS"]) {
|
|
62942
63050
|
const keyCount = process.env["OA_API_KEYS"].split(",").length;
|
|
@@ -68616,17 +68724,17 @@ async function evalCommand(opts, config) {
|
|
|
68616
68724
|
rawBackend = new FakeBackend();
|
|
68617
68725
|
}
|
|
68618
68726
|
const backend = {
|
|
68619
|
-
async complete(
|
|
68727
|
+
async complete(request) {
|
|
68620
68728
|
const result = await rawBackend.complete({
|
|
68621
|
-
messages:
|
|
68729
|
+
messages: request.messages.map((m) => ({
|
|
68622
68730
|
id: globalThis.crypto.randomUUID(),
|
|
68623
68731
|
sessionId: globalThis.crypto.randomUUID(),
|
|
68624
68732
|
role: m.role,
|
|
68625
68733
|
content: m.content,
|
|
68626
68734
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
68627
68735
|
})),
|
|
68628
|
-
maxTokens:
|
|
68629
|
-
temperature:
|
|
68736
|
+
maxTokens: request.maxTokens,
|
|
68737
|
+
temperature: request.temperature
|
|
68630
68738
|
});
|
|
68631
68739
|
return { content: result.content ?? "" };
|
|
68632
68740
|
}
|
package/package.json
CHANGED