@quilltap/plugin-utils 2.4.0 → 2.5.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/dist/index.mjs CHANGED
@@ -562,16 +562,21 @@ var OpenAICompatibleProvider = class {
562
562
  return out;
563
563
  }
564
564
  /**
565
- * Sends a message and returns the complete response.
565
+ * Build the Chat Completions request body — the ONE build both
566
+ * {@link sendMessage} and {@link streamMessage} call, so the streaming and
567
+ * non-streaming shapes cannot drift apart. Maps Quilltap messages to the
568
+ * OpenAI wire format (tool results and assistant tool calls included),
569
+ * applies {@link applySystemMessagePolicy}, assembles the body literal, and
570
+ * finishes with {@link applyProfileParams}.
566
571
  *
567
- * @param params - LLM parameters including messages, model, and settings
568
- * @param apiKey - API key for authentication
569
- * @returns Complete LLM response with content and usage statistics
572
+ * Subclasses that keep the base class's send/stream loops can override this
573
+ * to reshape the body in one place.
574
+ *
575
+ * @param params - LLM parameters for the request
576
+ * @param stream - Whether the body is for a streaming request; adds
577
+ * `stream: true` and `stream_options: { include_usage: true }`
570
578
  */
571
- async sendMessage(params, apiKey) {
572
- this.validateApiKeyRequirement(apiKey);
573
- const attachmentResults = this.collectAttachmentFailures(params);
574
- const client = this.createClient(apiKey);
579
+ buildRequestBody(params, stream) {
575
580
  const mappedMessages = params.messages.filter((m) => {
576
581
  if (m.role === "tool" && !m.toolCallId) return false;
577
582
  return true;
@@ -607,12 +612,30 @@ var OpenAICompatibleProvider = class {
607
612
  max_tokens: params.maxTokens ?? 4096,
608
613
  top_p: params.topP ?? 1,
609
614
  stop: params.stop,
615
+ // Spread here — not appended after the literal — so the streaming keys
616
+ // keep their historical position between `stop` and `user` and the
617
+ // serialized body stays byte-identical.
618
+ ...stream ? { stream: true, stream_options: { include_usage: true } } : {},
610
619
  ...params.cacheKey ? { user: params.cacheKey } : {},
611
620
  // Tools arrive only once the runtime gate (the profile's "Allow tool
612
621
  // use") has passed, so no separate guard is needed here.
613
622
  ...params.tools && params.tools.length > 0 ? { tools: params.tools, tool_choice: params.toolChoice ?? "auto" } : {}
614
623
  };
615
624
  this.applyProfileParams(body, params);
625
+ return body;
626
+ }
627
+ /**
628
+ * Sends a message and returns the complete response.
629
+ *
630
+ * @param params - LLM parameters including messages, model, and settings
631
+ * @param apiKey - API key for authentication
632
+ * @returns Complete LLM response with content and usage statistics
633
+ */
634
+ async sendMessage(params, apiKey) {
635
+ this.validateApiKeyRequirement(apiKey);
636
+ const attachmentResults = this.collectAttachmentFailures(params);
637
+ const client = this.createClient(apiKey);
638
+ const body = this.buildRequestBody(params, false);
616
639
  try {
617
640
  const response = await client.chat.completions.create(
618
641
  body,
@@ -652,47 +675,7 @@ var OpenAICompatibleProvider = class {
652
675
  this.validateApiKeyRequirement(apiKey);
653
676
  const attachmentResults = this.collectAttachmentFailures(params);
654
677
  const client = this.createClient(apiKey);
655
- const mappedMessages = params.messages.filter((m) => {
656
- if (m.role === "tool" && !m.toolCallId) return false;
657
- return true;
658
- }).map((m) => {
659
- if (m.role === "tool" && m.toolCallId) {
660
- return {
661
- role: "tool",
662
- tool_call_id: m.toolCallId,
663
- content: m.content
664
- };
665
- }
666
- if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
667
- return {
668
- role: "assistant",
669
- content: m.content || null,
670
- tool_calls: m.toolCalls.map((tc) => ({
671
- id: tc.id,
672
- type: tc.type,
673
- function: tc.function
674
- }))
675
- };
676
- }
677
- return {
678
- role: m.role,
679
- content: m.content
680
- };
681
- });
682
- const messages = this.applySystemMessagePolicy(mappedMessages);
683
- const body = {
684
- model: params.model,
685
- messages,
686
- temperature: params.temperature ?? 0.7,
687
- max_tokens: params.maxTokens ?? 4096,
688
- top_p: params.topP ?? 1,
689
- stop: params.stop,
690
- stream: true,
691
- stream_options: { include_usage: true },
692
- ...params.cacheKey ? { user: params.cacheKey } : {},
693
- ...params.tools && params.tools.length > 0 ? { tools: params.tools, tool_choice: params.toolChoice ?? "auto" } : {}
694
- };
695
- this.applyProfileParams(body, params);
678
+ const body = this.buildRequestBody(params, true);
696
679
  try {
697
680
  const stream = await client.chat.completions.create(
698
681
  body,
@@ -1061,7 +1044,7 @@ function rewriteLocalhostUrl(url) {
1061
1044
  }
1062
1045
 
1063
1046
  // src/version.generated.ts
1064
- var PLUGIN_UTILS_VERSION = "2.4.0";
1047
+ var PLUGIN_UTILS_VERSION = "2.5.0";
1065
1048
  export {
1066
1049
  BUILTIN_TOOL_NAMES,
1067
1050
  DEFAULT_REQUEST_TIMEOUT_MS,