@quilltap/plugin-utils 2.2.19 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +148 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +147 -24
- package/dist/index.mjs.map +1 -1
- package/dist/providers/index.d.mts +97 -1
- package/dist/providers/index.d.ts +97 -1
- package/dist/providers/index.js +147 -23
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/index.mjs +146 -23
- package/dist/providers/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -352,6 +352,21 @@ function buildRequestAbortSignal(params, defaultMs = DEFAULT_REQUEST_TIMEOUT_MS)
|
|
|
352
352
|
return AbortSignal.timeout(resolveRequestTimeoutMs(params, defaultMs));
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
+
// src/providers/profile-parameters.ts
|
|
356
|
+
function applyProfileParameters(body, params, allowlist, normalize) {
|
|
357
|
+
const profile = params.profileParameters;
|
|
358
|
+
if (!profile || typeof profile !== "object") return;
|
|
359
|
+
const bag = profile;
|
|
360
|
+
for (const key of allowlist) {
|
|
361
|
+
const raw = bag[key];
|
|
362
|
+
if (raw === void 0 || raw === null) continue;
|
|
363
|
+
if (typeof raw === "string" && raw === "") continue;
|
|
364
|
+
const value = normalize ? normalize(key, raw, params, body) : raw;
|
|
365
|
+
if (value === void 0) continue;
|
|
366
|
+
body[key] = value;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
355
370
|
// src/providers/openai-compatible.ts
|
|
356
371
|
var DEFAULT_MAX_RETRIES = 2;
|
|
357
372
|
var OpenAICompatibleProvider = class {
|
|
@@ -367,6 +382,16 @@ var OpenAICompatibleProvider = class {
|
|
|
367
382
|
this.supportedMimeTypes = [];
|
|
368
383
|
/** Web search is not supported */
|
|
369
384
|
this.supportsWebSearch = false;
|
|
385
|
+
/**
|
|
386
|
+
* Profile-parameter keys this provider forwards from
|
|
387
|
+
* `LLMParams.profileParameters` onto the request body.
|
|
388
|
+
*
|
|
389
|
+
* **Empty by default**, so a subclass that declares nothing sends exactly the
|
|
390
|
+
* bytes it sent before this mechanism existed. Subclasses opt in by
|
|
391
|
+
* overriding with their own allow-list; `model`, `messages`, `stream`,
|
|
392
|
+
* `stream_options` and `tools` must never appear in one.
|
|
393
|
+
*/
|
|
394
|
+
this.profileParamAllowlist = [];
|
|
370
395
|
if (typeof config === "string") {
|
|
371
396
|
this.baseUrl = config;
|
|
372
397
|
this.providerName = "OpenAICompatible";
|
|
@@ -454,6 +479,53 @@ var OpenAICompatibleProvider = class {
|
|
|
454
479
|
buildRequestOptions(params) {
|
|
455
480
|
return buildSdkRequestOptions(params);
|
|
456
481
|
}
|
|
482
|
+
/**
|
|
483
|
+
* Per-key hook for allow-listed profile parameters. The default forwards the
|
|
484
|
+
* stored value verbatim.
|
|
485
|
+
*
|
|
486
|
+
* Override to reshape a value (the editor stores flat strings where some APIs
|
|
487
|
+
* want objects), to gate a key on the model, or — by writing into `body` and
|
|
488
|
+
* returning `undefined` — to send it under a different key entirely.
|
|
489
|
+
*/
|
|
490
|
+
normalizeProfileParam(_key, value, _params, _body) {
|
|
491
|
+
return value;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Apply {@link profileParamAllowlist} to a request body under construction.
|
|
495
|
+
* Called by both body builds so the streaming and non-streaming shapes cannot
|
|
496
|
+
* drift apart.
|
|
497
|
+
*/
|
|
498
|
+
applyProfileParams(body, params) {
|
|
499
|
+
applyProfileParameters(
|
|
500
|
+
body,
|
|
501
|
+
params,
|
|
502
|
+
this.profileParamAllowlist,
|
|
503
|
+
(key, value, callParams, target) => this.normalizeProfileParam(key, value, callParams, target)
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Normalize an OpenAI-shaped `tool_calls` array into Quilltap's `ToolCall`
|
|
508
|
+
* shape, tolerating servers that hand back already-parsed argument objects
|
|
509
|
+
* where the OpenAI wire format specifies a JSON string.
|
|
510
|
+
*/
|
|
511
|
+
normalizeToolCalls(toolCalls) {
|
|
512
|
+
if (!toolCalls || toolCalls.length === 0) return [];
|
|
513
|
+
const out = [];
|
|
514
|
+
for (const raw of toolCalls) {
|
|
515
|
+
const tc = raw;
|
|
516
|
+
if (!tc?.function?.name) continue;
|
|
517
|
+
const args = tc.function.arguments;
|
|
518
|
+
out.push({
|
|
519
|
+
id: tc.id ?? "",
|
|
520
|
+
type: "function",
|
|
521
|
+
function: {
|
|
522
|
+
name: tc.function.name,
|
|
523
|
+
arguments: typeof args === "string" ? args : JSON.stringify(args ?? {})
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
457
529
|
/**
|
|
458
530
|
* Sends a message and returns the complete response.
|
|
459
531
|
*
|
|
@@ -492,17 +564,26 @@ var OpenAICompatibleProvider = class {
|
|
|
492
564
|
content: m.content
|
|
493
565
|
};
|
|
494
566
|
});
|
|
567
|
+
const body = {
|
|
568
|
+
model: params.model,
|
|
569
|
+
messages,
|
|
570
|
+
temperature: params.temperature ?? 0.7,
|
|
571
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
572
|
+
top_p: params.topP ?? 1,
|
|
573
|
+
stop: params.stop,
|
|
574
|
+
...params.cacheKey ? { user: params.cacheKey } : {},
|
|
575
|
+
// Tools arrive only once the runtime gate (the profile's "Allow tool
|
|
576
|
+
// use") has passed, so no separate guard is needed here.
|
|
577
|
+
...params.tools && params.tools.length > 0 ? { tools: params.tools, tool_choice: params.toolChoice ?? "auto" } : {}
|
|
578
|
+
};
|
|
579
|
+
this.applyProfileParams(body, params);
|
|
495
580
|
try {
|
|
496
|
-
const response = await client.chat.completions.create(
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
max_tokens: params.maxTokens ?? 4096,
|
|
501
|
-
top_p: params.topP ?? 1,
|
|
502
|
-
stop: params.stop,
|
|
503
|
-
...params.cacheKey ? { user: params.cacheKey } : {}
|
|
504
|
-
}, this.buildRequestOptions(params));
|
|
581
|
+
const response = await client.chat.completions.create(
|
|
582
|
+
body,
|
|
583
|
+
this.buildRequestOptions(params)
|
|
584
|
+
);
|
|
505
585
|
const choice = response.choices[0];
|
|
586
|
+
const toolCalls = this.normalizeToolCalls(choice.message.tool_calls);
|
|
506
587
|
return {
|
|
507
588
|
content: choice.message.content ?? "",
|
|
508
589
|
finishReason: choice.finish_reason,
|
|
@@ -512,7 +593,8 @@ var OpenAICompatibleProvider = class {
|
|
|
512
593
|
totalTokens: response.usage?.total_tokens ?? 0
|
|
513
594
|
},
|
|
514
595
|
raw: response,
|
|
515
|
-
attachmentResults
|
|
596
|
+
attachmentResults,
|
|
597
|
+
...toolCalls.length > 0 ? { toolCalls } : {}
|
|
516
598
|
};
|
|
517
599
|
} catch (error) {
|
|
518
600
|
this.logger.error(
|
|
@@ -561,23 +643,32 @@ var OpenAICompatibleProvider = class {
|
|
|
561
643
|
content: m.content
|
|
562
644
|
};
|
|
563
645
|
});
|
|
646
|
+
const body = {
|
|
647
|
+
model: params.model,
|
|
648
|
+
messages,
|
|
649
|
+
temperature: params.temperature ?? 0.7,
|
|
650
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
651
|
+
top_p: params.topP ?? 1,
|
|
652
|
+
stop: params.stop,
|
|
653
|
+
stream: true,
|
|
654
|
+
stream_options: { include_usage: true },
|
|
655
|
+
...params.cacheKey ? { user: params.cacheKey } : {},
|
|
656
|
+
...params.tools && params.tools.length > 0 ? { tools: params.tools, tool_choice: params.toolChoice ?? "auto" } : {}
|
|
657
|
+
};
|
|
658
|
+
this.applyProfileParams(body, params);
|
|
564
659
|
try {
|
|
565
|
-
const stream = await client.chat.completions.create(
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
max_tokens: params.maxTokens ?? 4096,
|
|
570
|
-
top_p: params.topP ?? 1,
|
|
571
|
-
stop: params.stop,
|
|
572
|
-
stream: true,
|
|
573
|
-
stream_options: { include_usage: true },
|
|
574
|
-
...params.cacheKey ? { user: params.cacheKey } : {}
|
|
575
|
-
}, this.buildRequestOptions(params));
|
|
660
|
+
const stream = await client.chat.completions.create(
|
|
661
|
+
body,
|
|
662
|
+
this.buildRequestOptions(params)
|
|
663
|
+
);
|
|
576
664
|
let chunkCount = 0;
|
|
577
665
|
let accumulatedUsage = null;
|
|
666
|
+
const toolCallAccumulator = /* @__PURE__ */ new Map();
|
|
667
|
+
let finishReason = null;
|
|
578
668
|
for await (const chunk of stream) {
|
|
579
669
|
chunkCount++;
|
|
580
|
-
const
|
|
670
|
+
const choice = chunk.choices[0];
|
|
671
|
+
const content = choice?.delta?.content;
|
|
581
672
|
const hasUsage = chunk.usage;
|
|
582
673
|
if (hasUsage) {
|
|
583
674
|
accumulatedUsage = {
|
|
@@ -586,6 +677,17 @@ var OpenAICompatibleProvider = class {
|
|
|
586
677
|
total_tokens: chunk.usage?.total_tokens
|
|
587
678
|
};
|
|
588
679
|
}
|
|
680
|
+
if (choice?.delta?.tool_calls) {
|
|
681
|
+
for (const tcDelta of choice.delta.tool_calls) {
|
|
682
|
+
const idx = tcDelta.index ?? 0;
|
|
683
|
+
const existing = toolCallAccumulator.get(idx) ?? { id: "", name: "", arguments: "" };
|
|
684
|
+
if (tcDelta.id) existing.id = tcDelta.id;
|
|
685
|
+
if (tcDelta.function?.name) existing.name = tcDelta.function.name;
|
|
686
|
+
if (tcDelta.function?.arguments) existing.arguments += tcDelta.function.arguments;
|
|
687
|
+
toolCallAccumulator.set(idx, existing);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
589
691
|
if (content) {
|
|
590
692
|
yield {
|
|
591
693
|
content,
|
|
@@ -593,6 +695,11 @@ var OpenAICompatibleProvider = class {
|
|
|
593
695
|
};
|
|
594
696
|
}
|
|
595
697
|
}
|
|
698
|
+
const toolCalls = Array.from(toolCallAccumulator.values()).filter((tc) => tc.name).map((tc) => ({
|
|
699
|
+
id: tc.id,
|
|
700
|
+
type: "function",
|
|
701
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
702
|
+
}));
|
|
596
703
|
yield {
|
|
597
704
|
content: "",
|
|
598
705
|
done: true,
|
|
@@ -601,7 +708,22 @@ var OpenAICompatibleProvider = class {
|
|
|
601
708
|
completionTokens: accumulatedUsage.completion_tokens ?? 0,
|
|
602
709
|
totalTokens: accumulatedUsage.total_tokens ?? 0
|
|
603
710
|
} : void 0,
|
|
604
|
-
attachmentResults
|
|
711
|
+
attachmentResults,
|
|
712
|
+
// Only synthesized when the stream actually carried tool calls: the
|
|
713
|
+
// host's tool detection reads `rawResponse`, and an empty one on every
|
|
714
|
+
// ordinary turn would be noise in the logs for no gain.
|
|
715
|
+
...toolCalls.length > 0 ? {
|
|
716
|
+
toolCalls,
|
|
717
|
+
rawResponse: {
|
|
718
|
+
choices: [
|
|
719
|
+
{
|
|
720
|
+
index: 0,
|
|
721
|
+
message: { role: "assistant", content: "", tool_calls: toolCalls },
|
|
722
|
+
finish_reason: finishReason
|
|
723
|
+
}
|
|
724
|
+
]
|
|
725
|
+
}
|
|
726
|
+
} : {}
|
|
605
727
|
};
|
|
606
728
|
} catch (error) {
|
|
607
729
|
this.logger.error(
|
|
@@ -902,7 +1024,7 @@ function rewriteLocalhostUrl(url) {
|
|
|
902
1024
|
}
|
|
903
1025
|
|
|
904
1026
|
// src/version.generated.ts
|
|
905
|
-
var PLUGIN_UTILS_VERSION = "2.
|
|
1027
|
+
var PLUGIN_UTILS_VERSION = "2.3.0";
|
|
906
1028
|
export {
|
|
907
1029
|
BUILTIN_TOOL_NAMES,
|
|
908
1030
|
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
@@ -913,6 +1035,7 @@ export {
|
|
|
913
1035
|
__injectCoreLoggerFactory,
|
|
914
1036
|
__injectQuilltapVersion,
|
|
915
1037
|
applyDescriptionLimit,
|
|
1038
|
+
applyProfileParameters,
|
|
916
1039
|
buildRequestAbortSignal,
|
|
917
1040
|
buildSdkClientOptions,
|
|
918
1041
|
buildSdkRequestOptions,
|