@quilltap/plugin-utils 2.2.20 → 2.4.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 CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to @quilltap/plugin-utils will be documented in this file.
4
4
 
5
+ ## [2.4.0] - 2026-08-19
6
+
7
+ ### Added
8
+
9
+ - `collapseLeadingSystemMessages(messages)` — folds a run of consecutive leading `system` messages into one, joining the contents with a blank line. Arrays with fewer than two leading system messages come back untouched (same reference).
10
+ - `OpenAICompatibleProvider` gained `acceptsRepeatedSystemMessages` (default `true`, so every existing subclass is byte-identical on the wire) and applies it through `applySystemMessagePolicy` in both the streaming and non-streaming body builds. A subclass pointing at a local runtime sets it `false`: the model's own chat template is what runs there, and the Qwen family — plus several Llama- and Gemma-derived templates — `raise_exception` on any system message after index 0, rejecting the whole request. Quilltap emits up to three leading system blocks so its cache breakpoints survive, so every non-opening turn against such a model failed outright. (Quilltap bug 82.)
11
+
12
+ ## [2.3.0] - 2026-08-15
13
+
14
+ ### Added
15
+
16
+ - `applyProfileParameters(body, params, allowlist, normalize?)` — the one mechanism providers use to forward a connection profile's free-form `parameters` blob onto a request body. Allow-listed (never spread, so `model` / `messages` / `stream` / `tools` stay unreachable from a profile); `undefined`, `null` and the empty string omit the key. Exported as a free function rather than a base-class method because only one plugin extends `OpenAICompatibleProvider` — Z.AI, OpenRouter and Ollama implement their providers directly and reach it by composition.
17
+ - `OpenAICompatibleProvider` gained `profileParamAllowlist` (empty by default, so every existing subclass is byte-identical on the wire until it opts in) and an overridable `normalizeProfileParam`, applied in both the streaming and non-streaming body builds.
18
+ - `OpenAICompatibleProvider` now sends `tools` / `tool_choice` when the caller supplies tools, and parses `tool_calls` back — from `choice.message.tool_calls` on the non-streaming path, and by accumulating index-keyed `delta.tool_calls` fragments while streaming. Previously it could never call a tool.
19
+
5
20
  ## [2.2.19] - 2026-08-03
6
21
 
7
22
  ### Fixed
package/README.md CHANGED
@@ -150,6 +150,37 @@ This gives you a complete `LLMProvider` implementation with:
150
150
  npm install openai
151
151
  ```
152
152
 
153
+ ### Profile Parameters
154
+
155
+ A connection profile's `parameters` blob is free-form JSON, handed to the
156
+ provider as `LLMParams.profileParameters`. A provider that never reads it drops
157
+ every setting the user saved, in silence — so forwarding is opt-in but explicit,
158
+ and always allow-listed:
159
+
160
+ ```typescript
161
+ import { applyProfileParameters } from '@quilltap/plugin-utils';
162
+
163
+ const MY_ALLOWLIST = ['top_k', 'min_p', 'reasoning_effort'] as const;
164
+
165
+ applyProfileParameters(body, params, MY_ALLOWLIST, (key, value, callParams, target) => {
166
+ // Return the value to send, or `undefined` to omit the key.
167
+ // Write into `target` to send it under a different key entirely.
168
+ return value;
169
+ });
170
+ ```
171
+
172
+ `undefined`, `null` and the empty string omit the key — the empty string is what
173
+ the schema-driven profile editor stores for "use the model default". Never
174
+ spread the blob: `model`, `messages`, `stream`, `stream_options` and `tools`
175
+ must stay unreachable from a profile.
176
+
177
+ Subclasses of `OpenAICompatibleProvider` get this by declaring
178
+ `profileParamAllowlist` (empty by default, so a subclass that declares nothing
179
+ is byte-identical on the wire) and optionally overriding `normalizeProfileParam`;
180
+ providers that implement `TextProvider` directly call the function themselves.
181
+ The keys must match whatever the plugin's `getProviderOptionsSchema()` renders,
182
+ or a field the editor draws goes nowhere.
183
+
153
184
  ### System Prompt Plugin Utilities
154
185
 
155
186
  Create system prompt plugins that provide character prompt templates from `.md` files:
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { AnthropicToolDefinition, CharacterPluginDataEntry, CharacterPluginDataMap, GoogleToolDefinition, LogContext, LogLevel, OpenAIToolDefinition, PluginLogger, ToolCall, ToolCallRequest, ToolFormatOptions, ToolResult, UniversalTool, createConsoleLogger, createNoopLogger } from '@quilltap/plugin-types';
2
2
  export { T as ToolCallFormat, a as ToolConvertTarget, b as applyDescriptionLimit, c as convertFromAnthropicFormat, d as convertFromGoogleFormat, e as convertOpenAIToAnthropicFormat, f as convertOpenAIToGoogleFormat, g as convertToAnthropicFormat, h as convertToGoogleFormat, i as convertToolTo, j as convertToolsTo, k as detectToolCallFormat, l as hasToolCalls, p as parseAnthropicToolCalls, m as parseGoogleToolCalls, n as parseOpenAIToolCalls, o as parseToolCalls } from './converters-BPYo8xdH.mjs';
3
3
  export { PluginLoggerWithChild, __clearCoreLoggerFactory, __injectCoreLoggerFactory, createPluginLogger, getLogLevelFromEnv, hasCoreLogger } from './logging/index.mjs';
4
- export { DEFAULT_REQUEST_TIMEOUT_MS, OpenAICompatibleProvider, OpenAICompatibleProviderConfig, buildRequestAbortSignal, buildSdkClientOptions, buildSdkRequestOptions, resolveRequestTimeoutMs } from './providers/index.mjs';
4
+ export { DEFAULT_REQUEST_TIMEOUT_MS, OpenAICompatibleProvider, OpenAICompatibleProviderConfig, ProfileParamNormalizer, applyProfileParameters, buildRequestAbortSignal, buildSdkClientOptions, buildSdkRequestOptions, collapseLeadingSystemMessages, resolveRequestTimeoutMs } from './providers/index.mjs';
5
5
  export { CreateSystemPromptPluginOptions, createSystemPromptPlugin, validateSystemPromptPlugin } from './system-prompts/index.mjs';
6
6
  export { isVMEnvironment, resolveHostGateway, rewriteLocalhostUrl } from './host-rewrite.mjs';
7
7
  import 'openai';
@@ -124,6 +124,6 @@ declare function getQuilltapUserAgent(): string;
124
124
  * Version of the plugin-utils package.
125
125
  * Can be used at runtime to check compatibility.
126
126
  */
127
- declare const PLUGIN_UTILS_VERSION = "2.2.20";
127
+ declare const PLUGIN_UTILS_VERSION = "2.4.0";
128
128
 
129
129
  export { BUILTIN_TOOL_NAMES, PLUGIN_UTILS_VERSION, __clearQuilltapVersion, __injectQuilltapVersion, getBuiltinToolNames, getQuilltapUserAgent, getQuilltapVersion };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { AnthropicToolDefinition, CharacterPluginDataEntry, CharacterPluginDataMap, GoogleToolDefinition, LogContext, LogLevel, OpenAIToolDefinition, PluginLogger, ToolCall, ToolCallRequest, ToolFormatOptions, ToolResult, UniversalTool, createConsoleLogger, createNoopLogger } from '@quilltap/plugin-types';
2
2
  export { T as ToolCallFormat, a as ToolConvertTarget, b as applyDescriptionLimit, c as convertFromAnthropicFormat, d as convertFromGoogleFormat, e as convertOpenAIToAnthropicFormat, f as convertOpenAIToGoogleFormat, g as convertToAnthropicFormat, h as convertToGoogleFormat, i as convertToolTo, j as convertToolsTo, k as detectToolCallFormat, l as hasToolCalls, p as parseAnthropicToolCalls, m as parseGoogleToolCalls, n as parseOpenAIToolCalls, o as parseToolCalls } from './converters-BPYo8xdH.js';
3
3
  export { PluginLoggerWithChild, __clearCoreLoggerFactory, __injectCoreLoggerFactory, createPluginLogger, getLogLevelFromEnv, hasCoreLogger } from './logging/index.js';
4
- export { DEFAULT_REQUEST_TIMEOUT_MS, OpenAICompatibleProvider, OpenAICompatibleProviderConfig, buildRequestAbortSignal, buildSdkClientOptions, buildSdkRequestOptions, resolveRequestTimeoutMs } from './providers/index.js';
4
+ export { DEFAULT_REQUEST_TIMEOUT_MS, OpenAICompatibleProvider, OpenAICompatibleProviderConfig, ProfileParamNormalizer, applyProfileParameters, buildRequestAbortSignal, buildSdkClientOptions, buildSdkRequestOptions, collapseLeadingSystemMessages, resolveRequestTimeoutMs } from './providers/index.js';
5
5
  export { CreateSystemPromptPluginOptions, createSystemPromptPlugin, validateSystemPromptPlugin } from './system-prompts/index.js';
6
6
  export { isVMEnvironment, resolveHostGateway, rewriteLocalhostUrl } from './host-rewrite.js';
7
7
  import 'openai';
@@ -124,6 +124,6 @@ declare function getQuilltapUserAgent(): string;
124
124
  * Version of the plugin-utils package.
125
125
  * Can be used at runtime to check compatibility.
126
126
  */
127
- declare const PLUGIN_UTILS_VERSION = "2.2.20";
127
+ declare const PLUGIN_UTILS_VERSION = "2.4.0";
128
128
 
129
129
  export { BUILTIN_TOOL_NAMES, PLUGIN_UTILS_VERSION, __clearQuilltapVersion, __injectQuilltapVersion, getBuiltinToolNames, getQuilltapUserAgent, getQuilltapVersion };
package/dist/index.js CHANGED
@@ -39,9 +39,11 @@ __export(src_exports, {
39
39
  __injectCoreLoggerFactory: () => __injectCoreLoggerFactory,
40
40
  __injectQuilltapVersion: () => __injectQuilltapVersion,
41
41
  applyDescriptionLimit: () => applyDescriptionLimit,
42
+ applyProfileParameters: () => applyProfileParameters,
42
43
  buildRequestAbortSignal: () => buildRequestAbortSignal,
43
44
  buildSdkClientOptions: () => buildSdkClientOptions,
44
45
  buildSdkRequestOptions: () => buildSdkRequestOptions,
46
+ collapseLeadingSystemMessages: () => collapseLeadingSystemMessages,
45
47
  convertFromAnthropicFormat: () => convertFromAnthropicFormat,
46
48
  convertFromGoogleFormat: () => convertFromGoogleFormat,
47
49
  convertOpenAIToAnthropicFormat: () => convertOpenAIToAnthropicFormat,
@@ -427,6 +429,36 @@ function buildRequestAbortSignal(params, defaultMs = DEFAULT_REQUEST_TIMEOUT_MS)
427
429
  return AbortSignal.timeout(resolveRequestTimeoutMs(params, defaultMs));
428
430
  }
429
431
 
432
+ // src/providers/profile-parameters.ts
433
+ function applyProfileParameters(body, params, allowlist, normalize) {
434
+ const profile = params.profileParameters;
435
+ if (!profile || typeof profile !== "object") return;
436
+ const bag = profile;
437
+ for (const key of allowlist) {
438
+ const raw = bag[key];
439
+ if (raw === void 0 || raw === null) continue;
440
+ if (typeof raw === "string" && raw === "") continue;
441
+ const value = normalize ? normalize(key, raw, params, body) : raw;
442
+ if (value === void 0) continue;
443
+ body[key] = value;
444
+ }
445
+ }
446
+
447
+ // src/providers/system-messages.ts
448
+ function collapseLeadingSystemMessages(messages) {
449
+ let runLength = 0;
450
+ while (runLength < messages.length && messages[runLength].role === "system") {
451
+ runLength++;
452
+ }
453
+ if (runLength < 2) return messages;
454
+ const run = messages.slice(0, runLength);
455
+ const merged = {
456
+ ...run[0],
457
+ content: run.map((m) => m.content ?? "").filter((c) => c.length > 0).join("\n\n")
458
+ };
459
+ return [merged, ...messages.slice(runLength)];
460
+ }
461
+
430
462
  // src/providers/openai-compatible.ts
431
463
  var DEFAULT_MAX_RETRIES = 2;
432
464
  var OpenAICompatibleProvider = class {
@@ -442,6 +474,28 @@ var OpenAICompatibleProvider = class {
442
474
  this.supportedMimeTypes = [];
443
475
  /** Web search is not supported */
444
476
  this.supportsWebSearch = false;
477
+ /**
478
+ * Whether the endpoint tolerates more than one leading `system` message.
479
+ *
480
+ * True for every hosted service — and so the default, which keeps the
481
+ * outbound bytes of OpenRouter, Z.AI and every other subclass exactly as they
482
+ * were. A subclass that points at a local runtime applying the *model's* chat
483
+ * template sets this false: several template families reject a system message
484
+ * anywhere but index 0, and Quilltap's context builder deliberately emits up
485
+ * to three of them so its cache breakpoints survive. See Bug 82 and
486
+ * {@link collapseLeadingSystemMessages}.
487
+ */
488
+ this.acceptsRepeatedSystemMessages = true;
489
+ /**
490
+ * Profile-parameter keys this provider forwards from
491
+ * `LLMParams.profileParameters` onto the request body.
492
+ *
493
+ * **Empty by default**, so a subclass that declares nothing sends exactly the
494
+ * bytes it sent before this mechanism existed. Subclasses opt in by
495
+ * overriding with their own allow-list; `model`, `messages`, `stream`,
496
+ * `stream_options` and `tools` must never appear in one.
497
+ */
498
+ this.profileParamAllowlist = [];
445
499
  if (typeof config === "string") {
446
500
  this.baseUrl = config;
447
501
  this.providerName = "OpenAICompatible";
@@ -459,6 +513,14 @@ var OpenAICompatibleProvider = class {
459
513
  }
460
514
  this.logger = createPluginLogger(`${this.providerName}Provider`);
461
515
  }
516
+ /**
517
+ * Apply {@link acceptsRepeatedSystemMessages} to a mapped wire-message array.
518
+ * Called by both body builds so the streaming and non-streaming shapes cannot
519
+ * drift apart.
520
+ */
521
+ applySystemMessagePolicy(messages) {
522
+ return this.acceptsRepeatedSystemMessages ? messages : collapseLeadingSystemMessages(messages);
523
+ }
462
524
  /**
463
525
  * Collects attachment failures for messages with attachments.
464
526
  * Since attachments are not supported, all attachments are marked as failed.
@@ -529,6 +591,53 @@ var OpenAICompatibleProvider = class {
529
591
  buildRequestOptions(params) {
530
592
  return buildSdkRequestOptions(params);
531
593
  }
594
+ /**
595
+ * Per-key hook for allow-listed profile parameters. The default forwards the
596
+ * stored value verbatim.
597
+ *
598
+ * Override to reshape a value (the editor stores flat strings where some APIs
599
+ * want objects), to gate a key on the model, or — by writing into `body` and
600
+ * returning `undefined` — to send it under a different key entirely.
601
+ */
602
+ normalizeProfileParam(_key, value, _params, _body) {
603
+ return value;
604
+ }
605
+ /**
606
+ * Apply {@link profileParamAllowlist} to a request body under construction.
607
+ * Called by both body builds so the streaming and non-streaming shapes cannot
608
+ * drift apart.
609
+ */
610
+ applyProfileParams(body, params) {
611
+ applyProfileParameters(
612
+ body,
613
+ params,
614
+ this.profileParamAllowlist,
615
+ (key, value, callParams, target) => this.normalizeProfileParam(key, value, callParams, target)
616
+ );
617
+ }
618
+ /**
619
+ * Normalize an OpenAI-shaped `tool_calls` array into Quilltap's `ToolCall`
620
+ * shape, tolerating servers that hand back already-parsed argument objects
621
+ * where the OpenAI wire format specifies a JSON string.
622
+ */
623
+ normalizeToolCalls(toolCalls) {
624
+ if (!toolCalls || toolCalls.length === 0) return [];
625
+ const out = [];
626
+ for (const raw of toolCalls) {
627
+ const tc = raw;
628
+ if (!tc?.function?.name) continue;
629
+ const args = tc.function.arguments;
630
+ out.push({
631
+ id: tc.id ?? "",
632
+ type: "function",
633
+ function: {
634
+ name: tc.function.name,
635
+ arguments: typeof args === "string" ? args : JSON.stringify(args ?? {})
636
+ }
637
+ });
638
+ }
639
+ return out;
640
+ }
532
641
  /**
533
642
  * Sends a message and returns the complete response.
534
643
  *
@@ -540,7 +649,7 @@ var OpenAICompatibleProvider = class {
540
649
  this.validateApiKeyRequirement(apiKey);
541
650
  const attachmentResults = this.collectAttachmentFailures(params);
542
651
  const client = this.createClient(apiKey);
543
- const messages = params.messages.filter((m) => {
652
+ const mappedMessages = params.messages.filter((m) => {
544
653
  if (m.role === "tool" && !m.toolCallId) return false;
545
654
  return true;
546
655
  }).map((m) => {
@@ -567,17 +676,27 @@ var OpenAICompatibleProvider = class {
567
676
  content: m.content
568
677
  };
569
678
  });
679
+ const messages = this.applySystemMessagePolicy(mappedMessages);
680
+ const body = {
681
+ model: params.model,
682
+ messages,
683
+ temperature: params.temperature ?? 0.7,
684
+ max_tokens: params.maxTokens ?? 4096,
685
+ top_p: params.topP ?? 1,
686
+ stop: params.stop,
687
+ ...params.cacheKey ? { user: params.cacheKey } : {},
688
+ // Tools arrive only once the runtime gate (the profile's "Allow tool
689
+ // use") has passed, so no separate guard is needed here.
690
+ ...params.tools && params.tools.length > 0 ? { tools: params.tools, tool_choice: params.toolChoice ?? "auto" } : {}
691
+ };
692
+ this.applyProfileParams(body, params);
570
693
  try {
571
- const response = await client.chat.completions.create({
572
- model: params.model,
573
- messages,
574
- temperature: params.temperature ?? 0.7,
575
- max_tokens: params.maxTokens ?? 4096,
576
- top_p: params.topP ?? 1,
577
- stop: params.stop,
578
- ...params.cacheKey ? { user: params.cacheKey } : {}
579
- }, this.buildRequestOptions(params));
694
+ const response = await client.chat.completions.create(
695
+ body,
696
+ this.buildRequestOptions(params)
697
+ );
580
698
  const choice = response.choices[0];
699
+ const toolCalls = this.normalizeToolCalls(choice.message.tool_calls);
581
700
  return {
582
701
  content: choice.message.content ?? "",
583
702
  finishReason: choice.finish_reason,
@@ -587,7 +706,8 @@ var OpenAICompatibleProvider = class {
587
706
  totalTokens: response.usage?.total_tokens ?? 0
588
707
  },
589
708
  raw: response,
590
- attachmentResults
709
+ attachmentResults,
710
+ ...toolCalls.length > 0 ? { toolCalls } : {}
591
711
  };
592
712
  } catch (error) {
593
713
  this.logger.error(
@@ -609,7 +729,7 @@ var OpenAICompatibleProvider = class {
609
729
  this.validateApiKeyRequirement(apiKey);
610
730
  const attachmentResults = this.collectAttachmentFailures(params);
611
731
  const client = this.createClient(apiKey);
612
- const messages = params.messages.filter((m) => {
732
+ const mappedMessages = params.messages.filter((m) => {
613
733
  if (m.role === "tool" && !m.toolCallId) return false;
614
734
  return true;
615
735
  }).map((m) => {
@@ -636,23 +756,33 @@ var OpenAICompatibleProvider = class {
636
756
  content: m.content
637
757
  };
638
758
  });
759
+ const messages = this.applySystemMessagePolicy(mappedMessages);
760
+ const body = {
761
+ model: params.model,
762
+ messages,
763
+ temperature: params.temperature ?? 0.7,
764
+ max_tokens: params.maxTokens ?? 4096,
765
+ top_p: params.topP ?? 1,
766
+ stop: params.stop,
767
+ stream: true,
768
+ stream_options: { include_usage: true },
769
+ ...params.cacheKey ? { user: params.cacheKey } : {},
770
+ ...params.tools && params.tools.length > 0 ? { tools: params.tools, tool_choice: params.toolChoice ?? "auto" } : {}
771
+ };
772
+ this.applyProfileParams(body, params);
639
773
  try {
640
- const stream = await client.chat.completions.create({
641
- model: params.model,
642
- messages,
643
- temperature: params.temperature ?? 0.7,
644
- max_tokens: params.maxTokens ?? 4096,
645
- top_p: params.topP ?? 1,
646
- stop: params.stop,
647
- stream: true,
648
- stream_options: { include_usage: true },
649
- ...params.cacheKey ? { user: params.cacheKey } : {}
650
- }, this.buildRequestOptions(params));
774
+ const stream = await client.chat.completions.create(
775
+ body,
776
+ this.buildRequestOptions(params)
777
+ );
651
778
  let chunkCount = 0;
652
779
  let accumulatedUsage = null;
780
+ const toolCallAccumulator = /* @__PURE__ */ new Map();
781
+ let finishReason = null;
653
782
  for await (const chunk of stream) {
654
783
  chunkCount++;
655
- const content = chunk.choices[0]?.delta?.content;
784
+ const choice = chunk.choices[0];
785
+ const content = choice?.delta?.content;
656
786
  const hasUsage = chunk.usage;
657
787
  if (hasUsage) {
658
788
  accumulatedUsage = {
@@ -661,6 +791,17 @@ var OpenAICompatibleProvider = class {
661
791
  total_tokens: chunk.usage?.total_tokens
662
792
  };
663
793
  }
794
+ if (choice?.delta?.tool_calls) {
795
+ for (const tcDelta of choice.delta.tool_calls) {
796
+ const idx = tcDelta.index ?? 0;
797
+ const existing = toolCallAccumulator.get(idx) ?? { id: "", name: "", arguments: "" };
798
+ if (tcDelta.id) existing.id = tcDelta.id;
799
+ if (tcDelta.function?.name) existing.name = tcDelta.function.name;
800
+ if (tcDelta.function?.arguments) existing.arguments += tcDelta.function.arguments;
801
+ toolCallAccumulator.set(idx, existing);
802
+ }
803
+ }
804
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
664
805
  if (content) {
665
806
  yield {
666
807
  content,
@@ -668,6 +809,11 @@ var OpenAICompatibleProvider = class {
668
809
  };
669
810
  }
670
811
  }
812
+ const toolCalls = Array.from(toolCallAccumulator.values()).filter((tc) => tc.name).map((tc) => ({
813
+ id: tc.id,
814
+ type: "function",
815
+ function: { name: tc.name, arguments: tc.arguments }
816
+ }));
671
817
  yield {
672
818
  content: "",
673
819
  done: true,
@@ -676,7 +822,22 @@ var OpenAICompatibleProvider = class {
676
822
  completionTokens: accumulatedUsage.completion_tokens ?? 0,
677
823
  totalTokens: accumulatedUsage.total_tokens ?? 0
678
824
  } : void 0,
679
- attachmentResults
825
+ attachmentResults,
826
+ // Only synthesized when the stream actually carried tool calls: the
827
+ // host's tool detection reads `rawResponse`, and an empty one on every
828
+ // ordinary turn would be noise in the logs for no gain.
829
+ ...toolCalls.length > 0 ? {
830
+ toolCalls,
831
+ rawResponse: {
832
+ choices: [
833
+ {
834
+ index: 0,
835
+ message: { role: "assistant", content: "", tool_calls: toolCalls },
836
+ finish_reason: finishReason
837
+ }
838
+ ]
839
+ }
840
+ } : {}
680
841
  };
681
842
  } catch (error) {
682
843
  this.logger.error(
@@ -977,7 +1138,7 @@ function rewriteLocalhostUrl(url) {
977
1138
  }
978
1139
 
979
1140
  // src/version.generated.ts
980
- var PLUGIN_UTILS_VERSION = "2.2.20";
1141
+ var PLUGIN_UTILS_VERSION = "2.4.0";
981
1142
  // Annotate the CommonJS export names for ESM import in node:
982
1143
  0 && (module.exports = {
983
1144
  BUILTIN_TOOL_NAMES,
@@ -989,9 +1150,11 @@ var PLUGIN_UTILS_VERSION = "2.2.20";
989
1150
  __injectCoreLoggerFactory,
990
1151
  __injectQuilltapVersion,
991
1152
  applyDescriptionLimit,
1153
+ applyProfileParameters,
992
1154
  buildRequestAbortSignal,
993
1155
  buildSdkClientOptions,
994
1156
  buildSdkRequestOptions,
1157
+ collapseLeadingSystemMessages,
995
1158
  convertFromAnthropicFormat,
996
1159
  convertFromGoogleFormat,
997
1160
  convertOpenAIToAnthropicFormat,