@psnext/slingcli 2.4.20260601-1 → 2.5.20260602-2
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/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/package.json +4 -4
- package/node_modules/@aws-sdk/credential-provider-node/dist-cjs/index.js +12 -1
- package/node_modules/@aws-sdk/credential-provider-node/dist-es/runtime/memoize-chain.js +12 -1
- package/node_modules/@aws-sdk/credential-provider-node/package.json +1 -1
- package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +81 -18
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +14 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +197 -190
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +84 -21
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +24 -14
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +5 -9
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.js +5 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.js +2 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.js +2 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +130 -62
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +18 -12
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js +4 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +5 -9
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/stream.js +14 -2
- package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.js +34 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js +8 -7
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +179 -79
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +27 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +9 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +29 -5
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js +21 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/footer-data-provider.js +29 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +65 -13
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.js +134 -11
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +12 -6
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +167 -96
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +14 -9
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +0 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js +7 -10
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +5 -7
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +6 -7
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.js +17 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +5 -6
- package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +69 -16
- package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js +118 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +2 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +61 -6
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/print-mode.js +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +4 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.js +13 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.js +7 -0
- package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +71 -56
- package/node_modules/@earendil-works/pi-coding-agent/package.json +7 -7
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +9 -53
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js +6 -54
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +34 -7
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +172 -37
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +166 -22
- package/node_modules/@earendil-works/pi-tui/dist/utils.js +8 -3
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js +96 -0
- package/node_modules/@earendil-works/pi-tui/package.json +2 -2
- package/node_modules/@mariozechner/clipboard/Cargo.toml +3 -3
- package/node_modules/@mariozechner/clipboard/index.d.ts +34 -20
- package/node_modules/@mariozechner/clipboard/index.js +546 -257
- package/node_modules/@mariozechner/clipboard/package.json +14 -14
- package/node_modules/@mariozechner/clipboard/src/lib.rs +4 -9
- package/node_modules/@mariozechner/clipboard-linux-x64-gnu/clipboard.linux-x64-gnu.node +0 -0
- package/node_modules/@mariozechner/clipboard-linux-x64-gnu/package.json +2 -2
- package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.browser.js +39 -22
- package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.js +39 -22
- package/node_modules/@smithy/core/dist-es/submodules/retry/middleware-retry/configurations.js +19 -6
- package/node_modules/@smithy/core/dist-es/submodules/retry/middleware-retry/retryMiddleware.js +4 -5
- package/node_modules/@smithy/core/dist-es/submodules/retry/service-error-classification/constants.js +1 -1
- package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/ConfiguredRetryStrategy.js +4 -5
- package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/DefaultRetryToken.js +3 -0
- package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/StandardRetryStrategy.js +9 -5
- package/node_modules/@smithy/core/dist-types/submodules/retry/middleware-retry/configurations.d.ts +4 -1
- package/node_modules/@smithy/core/dist-types/submodules/retry/util-retry/ConfiguredRetryStrategy.d.ts +1 -2
- package/node_modules/@smithy/core/dist-types/submodules/retry/util-retry/DefaultRetryToken.d.ts +3 -0
- package/node_modules/@smithy/core/dist-types/submodules/retry/util-retry/StandardRetryStrategy.d.ts +1 -1
- package/node_modules/@smithy/core/package.json +2 -2
- package/node_modules/@smithy/credential-provider-imds/package.json +3 -3
- package/node_modules/@smithy/fetch-http-handler/package.json +4 -4
- package/node_modules/@smithy/signature-v4/package.json +3 -3
- package/node_modules/@smithy/types/dist-types/retry.d.ts +25 -0
- package/node_modules/@smithy/types/package.json +1 -1
- package/package.json +6 -6
- package/slingshot/index.js +231 -230
|
@@ -7,6 +7,7 @@ import { createHttpProxyAgentsForTarget } from "../utils/node-http-proxy.js";
|
|
|
7
7
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
8
8
|
import { adjustMaxTokensForThinking, buildBaseOptions, clampReasoning } from "./simple-options.js";
|
|
9
9
|
import { transformMessages } from "./transform-messages.js";
|
|
10
|
+
const EMPTY_TEXT_PLACEHOLDER = "<empty>";
|
|
10
11
|
export const streamBedrock = (model, context, options = {}) => {
|
|
11
12
|
const stream = new AssistantMessageEventStream();
|
|
12
13
|
(async () => {
|
|
@@ -89,6 +90,9 @@ export const streamBedrock = (model, context, options = {}) => {
|
|
|
89
90
|
}
|
|
90
91
|
try {
|
|
91
92
|
const client = new BedrockRuntimeClient(config);
|
|
93
|
+
if (options.headers && Object.keys(options.headers).length > 0) {
|
|
94
|
+
addCustomHeadersMiddleware(client, options.headers);
|
|
95
|
+
}
|
|
92
96
|
const cacheRetention = resolveCacheRetention(options.cacheRetention);
|
|
93
97
|
const inferenceMaxTokens = options.maxTokens ?? (isAnthropicClaudeModel(model) ? model.maxTokens : undefined);
|
|
94
98
|
let commandInput = {
|
|
@@ -205,6 +209,39 @@ function formatBedrockError(error) {
|
|
|
205
209
|
}
|
|
206
210
|
return message;
|
|
207
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Header keys that must never be overwritten by caller-supplied headers.
|
|
214
|
+
* `host` and `x-amz-*` participate in the SigV4 canonical request; `authorization`
|
|
215
|
+
* is owned by SigV4 or the bearer-token path (config.token + authSchemePreference).
|
|
216
|
+
* Compared case-insensitively (caller key is lower-cased before lookup).
|
|
217
|
+
*/
|
|
218
|
+
const RESERVED_HEADER_EXACT = new Set(["authorization", "host"]);
|
|
219
|
+
function isReservedHeader(key) {
|
|
220
|
+
const lower = key.toLowerCase();
|
|
221
|
+
return lower.startsWith("x-amz-") || RESERVED_HEADER_EXACT.has(lower);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Attach caller-supplied headers to the outgoing Bedrock request via a Smithy
|
|
225
|
+
* `build`-step middleware. The `build` step runs after request serialisation but
|
|
226
|
+
* before SigV4 signing, so injected headers are covered by the signature. Reserved
|
|
227
|
+
* SigV4 / auth headers (`x-amz-*`, `authorization`, `host`) are silently skipped;
|
|
228
|
+
* all other caller headers override any existing same-named header on the request.
|
|
229
|
+
*/
|
|
230
|
+
function addCustomHeadersMiddleware(client, headers) {
|
|
231
|
+
const middleware = (next) => async (args) => {
|
|
232
|
+
const request = args.request;
|
|
233
|
+
if (request && typeof request === "object" && "headers" in request) {
|
|
234
|
+
const requestHeaders = request.headers;
|
|
235
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
236
|
+
if (!isReservedHeader(key)) {
|
|
237
|
+
requestHeaders[key] = value;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return next(args);
|
|
242
|
+
};
|
|
243
|
+
client.middlewareStack.add(middleware, { step: "build", name: "pi-ai-custom-headers", priority: "low" });
|
|
244
|
+
}
|
|
208
245
|
export const streamSimpleBedrock = (model, context, options) => {
|
|
209
246
|
const base = buildBaseOptions(model, options, undefined);
|
|
210
247
|
if (!options?.reasoning) {
|
|
@@ -350,11 +387,11 @@ function getModelMatchCandidates(modelId, modelName) {
|
|
|
350
387
|
}
|
|
351
388
|
function supportsAdaptiveThinking(modelId, modelName) {
|
|
352
389
|
const candidates = getModelMatchCandidates(modelId, modelName);
|
|
353
|
-
return candidates.some((s) => s.includes("opus-4-6") || s.includes("opus-4-7") || s.includes("sonnet-4-6"));
|
|
390
|
+
return candidates.some((s) => s.includes("opus-4-6") || s.includes("opus-4-7") || s.includes("opus-4-8") || s.includes("sonnet-4-6"));
|
|
354
391
|
}
|
|
355
392
|
function supportsNativeXhighEffort(model) {
|
|
356
393
|
const candidates = getModelMatchCandidates(model.id, model.name);
|
|
357
|
-
return candidates.some((s) => s.includes("opus-4-7"));
|
|
394
|
+
return candidates.some((s) => s.includes("opus-4-7") || s.includes("opus-4-8"));
|
|
358
395
|
}
|
|
359
396
|
function mapThinkingLevelToEffort(model, level) {
|
|
360
397
|
if (level === "xhigh" && supportsNativeXhighEffort(model))
|
|
@@ -461,6 +498,29 @@ function normalizeToolCallId(id) {
|
|
|
461
498
|
const sanitized = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
462
499
|
return sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized;
|
|
463
500
|
}
|
|
501
|
+
function createNonBlankTextBlock(text) {
|
|
502
|
+
const sanitized = sanitizeSurrogates(text);
|
|
503
|
+
return sanitized.trim().length === 0 ? undefined : { text: sanitized };
|
|
504
|
+
}
|
|
505
|
+
function createRequiredTextBlock(text) {
|
|
506
|
+
return createNonBlankTextBlock(text) ?? { text: EMPTY_TEXT_PLACEHOLDER };
|
|
507
|
+
}
|
|
508
|
+
function convertToolResultContent(content) {
|
|
509
|
+
const result = [];
|
|
510
|
+
for (const c of content) {
|
|
511
|
+
if (c.type === "image") {
|
|
512
|
+
result.push({ image: createImageBlock(c.mimeType, c.data) });
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
const textBlock = createNonBlankTextBlock(c.text);
|
|
516
|
+
if (textBlock)
|
|
517
|
+
result.push(textBlock);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (result.length === 0)
|
|
521
|
+
result.push({ text: EMPTY_TEXT_PLACEHOLDER });
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
464
524
|
function convertMessages(context, model, cacheRetention) {
|
|
465
525
|
const result = [];
|
|
466
526
|
const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
|
|
@@ -470,14 +530,17 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
470
530
|
case "user": {
|
|
471
531
|
const content = [];
|
|
472
532
|
if (typeof m.content === "string") {
|
|
473
|
-
content.push(
|
|
533
|
+
content.push(createRequiredTextBlock(m.content));
|
|
474
534
|
}
|
|
475
535
|
else {
|
|
476
536
|
for (const c of m.content) {
|
|
477
537
|
switch (c.type) {
|
|
478
|
-
case "text":
|
|
479
|
-
|
|
538
|
+
case "text": {
|
|
539
|
+
const textBlock = createNonBlankTextBlock(c.text);
|
|
540
|
+
if (textBlock)
|
|
541
|
+
content.push(textBlock);
|
|
480
542
|
break;
|
|
543
|
+
}
|
|
481
544
|
case "image":
|
|
482
545
|
content.push({ image: createImageBlock(c.mimeType, c.data) });
|
|
483
546
|
break;
|
|
@@ -485,9 +548,9 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
485
548
|
continue;
|
|
486
549
|
}
|
|
487
550
|
}
|
|
551
|
+
if (content.length === 0)
|
|
552
|
+
content.push({ text: EMPTY_TEXT_PLACEHOLDER });
|
|
488
553
|
}
|
|
489
|
-
if (content.length === 0)
|
|
490
|
-
continue;
|
|
491
554
|
result.push({
|
|
492
555
|
role: ConversationRole.USER,
|
|
493
556
|
content,
|
|
@@ -503,20 +566,23 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
503
566
|
const contentBlocks = [];
|
|
504
567
|
for (const c of m.content) {
|
|
505
568
|
switch (c.type) {
|
|
506
|
-
case "text":
|
|
569
|
+
case "text": {
|
|
507
570
|
// Skip empty text blocks
|
|
508
|
-
|
|
571
|
+
const textBlock = createNonBlankTextBlock(c.text);
|
|
572
|
+
if (!textBlock)
|
|
509
573
|
continue;
|
|
510
|
-
contentBlocks.push(
|
|
574
|
+
contentBlocks.push(textBlock);
|
|
511
575
|
break;
|
|
576
|
+
}
|
|
512
577
|
case "toolCall":
|
|
513
578
|
contentBlocks.push({
|
|
514
579
|
toolUse: { toolUseId: c.id, name: c.name, input: c.arguments },
|
|
515
580
|
});
|
|
516
581
|
break;
|
|
517
|
-
case "thinking":
|
|
582
|
+
case "thinking": {
|
|
518
583
|
// Skip empty thinking blocks
|
|
519
|
-
|
|
584
|
+
const thinking = sanitizeSurrogates(c.thinking);
|
|
585
|
+
if (thinking.trim().length === 0)
|
|
520
586
|
continue;
|
|
521
587
|
// Only Anthropic models support the signature field in reasoningText.
|
|
522
588
|
// For other models, we omit the signature to avoid errors like:
|
|
@@ -526,13 +592,13 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
526
592
|
// persisted message lacks a signature, Bedrock rejects the replayed
|
|
527
593
|
// reasoning block. Fall back to plain text, matching Anthropic.
|
|
528
594
|
if (!c.thinkingSignature || c.thinkingSignature.trim().length === 0) {
|
|
529
|
-
contentBlocks.push({ text:
|
|
595
|
+
contentBlocks.push({ text: thinking });
|
|
530
596
|
}
|
|
531
597
|
else {
|
|
532
598
|
contentBlocks.push({
|
|
533
599
|
reasoningContent: {
|
|
534
600
|
reasoningText: {
|
|
535
|
-
text:
|
|
601
|
+
text: thinking,
|
|
536
602
|
signature: c.thinkingSignature,
|
|
537
603
|
},
|
|
538
604
|
},
|
|
@@ -542,11 +608,12 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
542
608
|
else {
|
|
543
609
|
contentBlocks.push({
|
|
544
610
|
reasoningContent: {
|
|
545
|
-
reasoningText: { text:
|
|
611
|
+
reasoningText: { text: thinking },
|
|
546
612
|
},
|
|
547
613
|
});
|
|
548
614
|
}
|
|
549
615
|
break;
|
|
616
|
+
}
|
|
550
617
|
default:
|
|
551
618
|
continue;
|
|
552
619
|
}
|
|
@@ -569,9 +636,7 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
569
636
|
toolResults.push({
|
|
570
637
|
toolResult: {
|
|
571
638
|
toolUseId: m.toolCallId,
|
|
572
|
-
content: m.content
|
|
573
|
-
? { image: createImageBlock(c.mimeType, c.data) }
|
|
574
|
-
: { text: sanitizeSurrogates(c.text) }),
|
|
639
|
+
content: convertToolResultContent(m.content),
|
|
575
640
|
status: m.isError ? ToolResultStatus.ERROR : ToolResultStatus.SUCCESS,
|
|
576
641
|
},
|
|
577
642
|
});
|
|
@@ -582,9 +647,7 @@ function convertMessages(context, model, cacheRetention) {
|
|
|
582
647
|
toolResults.push({
|
|
583
648
|
toolResult: {
|
|
584
649
|
toolUseId: nextMsg.toolCallId,
|
|
585
|
-
content: nextMsg.content
|
|
586
|
-
? { image: createImageBlock(c.mimeType, c.data) }
|
|
587
|
-
: { text: sanitizeSurrogates(c.text) }),
|
|
650
|
+
content: convertToolResultContent(nextMsg.content),
|
|
588
651
|
status: nextMsg.isError ? ToolResultStatus.ERROR : ToolResultStatus.SUCCESS,
|
|
589
652
|
},
|
|
590
653
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
-
import { getEnvApiKey } from "../env-api-keys.js";
|
|
3
2
|
import { calculateCost } from "../models.js";
|
|
4
3
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
5
4
|
import { headersToRecord } from "../utils/headers.js";
|
|
@@ -116,6 +115,8 @@ function getAnthropicCompat(model) {
|
|
|
116
115
|
supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? !isFireworks,
|
|
117
116
|
sendSessionAffinityHeaders: model.compat?.sendSessionAffinityHeaders ?? !!(isFireworks || isCloudflareAiGatewayAnthropic),
|
|
118
117
|
supportsCacheControlOnTools: model.compat?.supportsCacheControlOnTools ?? !isFireworks,
|
|
118
|
+
supportsTemperature: model.compat?.supportsTemperature ?? true,
|
|
119
|
+
allowEmptySignature: model.compat?.allowEmptySignature ?? false,
|
|
119
120
|
};
|
|
120
121
|
}
|
|
121
122
|
function mergeHeaders(...headerSources) {
|
|
@@ -306,7 +307,10 @@ export const streamAnthropic = (model, context, options) => {
|
|
|
306
307
|
isOAuth = false;
|
|
307
308
|
}
|
|
308
309
|
else {
|
|
309
|
-
const apiKey = options?.apiKey
|
|
310
|
+
const apiKey = options?.apiKey;
|
|
311
|
+
if (!apiKey) {
|
|
312
|
+
throw new Error(`No API key for provider: ${model.provider}`);
|
|
313
|
+
}
|
|
310
314
|
let copilotDynamicHeaders;
|
|
311
315
|
if (model.provider === "github-copilot") {
|
|
312
316
|
const hasImages = hasCopilotVisionInput(context.messages);
|
|
@@ -548,7 +552,7 @@ function mapThinkingLevelToEffort(model, level) {
|
|
|
548
552
|
}
|
|
549
553
|
}
|
|
550
554
|
export const streamSimpleAnthropic = (model, context, options) => {
|
|
551
|
-
const apiKey = options?.apiKey
|
|
555
|
+
const apiKey = options?.apiKey;
|
|
552
556
|
if (!apiKey) {
|
|
553
557
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
554
558
|
}
|
|
@@ -655,9 +659,10 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
|
|
|
655
659
|
}
|
|
656
660
|
function buildParams(model, context, isOAuthToken, options) {
|
|
657
661
|
const { cacheControl } = getCacheControl(model, options?.cacheRetention);
|
|
662
|
+
const compat = getAnthropicCompat(model);
|
|
658
663
|
const params = {
|
|
659
664
|
model: model.id,
|
|
660
|
-
messages: convertMessages(context.messages, model, isOAuthToken, cacheControl),
|
|
665
|
+
messages: convertMessages(context.messages, model, isOAuthToken, cacheControl, compat.allowEmptySignature),
|
|
661
666
|
max_tokens: options?.maxTokens ?? model.maxTokens,
|
|
662
667
|
stream: true,
|
|
663
668
|
};
|
|
@@ -688,12 +693,11 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
688
693
|
},
|
|
689
694
|
];
|
|
690
695
|
}
|
|
691
|
-
// Temperature is incompatible with extended thinking
|
|
692
|
-
if (options?.temperature !== undefined && !options?.thinkingEnabled) {
|
|
696
|
+
// Temperature is incompatible with extended thinking and unsupported on Claude Opus 4.7+.
|
|
697
|
+
if (options?.temperature !== undefined && !options?.thinkingEnabled && compat.supportsTemperature) {
|
|
693
698
|
params.temperature = options.temperature;
|
|
694
699
|
}
|
|
695
700
|
if (context.tools && context.tools.length > 0) {
|
|
696
|
-
const compat = getAnthropicCompat(model);
|
|
697
701
|
params.tools = convertTools(context.tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsCacheControlOnTools ? cacheControl : undefined);
|
|
698
702
|
}
|
|
699
703
|
// Configure thinking mode: adaptive, budget-based, or explicitly disabled.
|
|
@@ -746,7 +750,7 @@ function buildParams(model, context, isOAuthToken, options) {
|
|
|
746
750
|
function normalizeToolCallId(id) {
|
|
747
751
|
return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
748
752
|
}
|
|
749
|
-
function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
753
|
+
function convertMessages(messages, model, isOAuthToken, cacheControl, allowEmptySignature = false) {
|
|
750
754
|
const params = [];
|
|
751
755
|
// Transform messages for cross-provider compatibility
|
|
752
756
|
const transformedMessages = transformMessages(messages, model, normalizeToolCallId);
|
|
@@ -817,13 +821,19 @@ function convertMessages(messages, model, isOAuthToken, cacheControl) {
|
|
|
817
821
|
if (block.thinking.trim().length === 0)
|
|
818
822
|
continue;
|
|
819
823
|
// If thinking signature is missing/empty (e.g., from aborted stream),
|
|
820
|
-
// convert to plain text
|
|
821
|
-
// and
|
|
824
|
+
// convert to plain text for Anthropic. Some compatible providers emit
|
|
825
|
+
// and accept empty signatures, so let marked models preserve the block.
|
|
822
826
|
if (!block.thinkingSignature || block.thinkingSignature.trim().length === 0) {
|
|
823
|
-
blocks.push(
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
+
blocks.push(allowEmptySignature
|
|
828
|
+
? {
|
|
829
|
+
type: "thinking",
|
|
830
|
+
thinking: sanitizeSurrogates(block.thinking),
|
|
831
|
+
signature: "",
|
|
832
|
+
}
|
|
833
|
+
: {
|
|
834
|
+
type: "text",
|
|
835
|
+
text: sanitizeSurrogates(block.thinking),
|
|
836
|
+
});
|
|
827
837
|
}
|
|
828
838
|
else {
|
|
829
839
|
blocks.push({
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AzureOpenAI } from "openai";
|
|
2
|
-
import { getEnvApiKey } from "../env-api-keys.js";
|
|
3
2
|
import { clampThinkingLevel } from "../models.js";
|
|
4
3
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
5
4
|
import { headersToRecord } from "../utils/headers.js";
|
|
@@ -73,7 +72,10 @@ export const streamAzureOpenAIResponses = (model, context, options) => {
|
|
|
73
72
|
};
|
|
74
73
|
try {
|
|
75
74
|
// Create Azure OpenAI client
|
|
76
|
-
const apiKey = options?.apiKey
|
|
75
|
+
const apiKey = options?.apiKey;
|
|
76
|
+
if (!apiKey) {
|
|
77
|
+
throw new Error(`No API key for provider: ${model.provider}`);
|
|
78
|
+
}
|
|
77
79
|
const client = createClient(model, apiKey, options);
|
|
78
80
|
let params = buildParams(model, context, options, deploymentName);
|
|
79
81
|
const nextParams = await options?.onPayload?.(params, model);
|
|
@@ -113,7 +115,7 @@ export const streamAzureOpenAIResponses = (model, context, options) => {
|
|
|
113
115
|
return stream;
|
|
114
116
|
};
|
|
115
117
|
export const streamSimpleAzureOpenAIResponses = (model, context, options) => {
|
|
116
|
-
const apiKey = options?.apiKey
|
|
118
|
+
const apiKey = options?.apiKey;
|
|
117
119
|
if (!apiKey) {
|
|
118
120
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
119
121
|
}
|
|
@@ -167,12 +169,6 @@ function resolveAzureConfig(model, options) {
|
|
|
167
169
|
};
|
|
168
170
|
}
|
|
169
171
|
function createClient(model, apiKey, options) {
|
|
170
|
-
if (!apiKey) {
|
|
171
|
-
if (!process.env.AZURE_OPENAI_API_KEY) {
|
|
172
|
-
throw new Error("Azure OpenAI API key is required. Set AZURE_OPENAI_API_KEY environment variable or pass it as an argument.");
|
|
173
|
-
}
|
|
174
|
-
apiKey = process.env.AZURE_OPENAI_API_KEY;
|
|
175
|
-
}
|
|
176
172
|
const headers = { ...model.headers };
|
|
177
173
|
if (options?.headers) {
|
|
178
174
|
Object.assign(headers, options.headers);
|
|
@@ -300,7 +300,7 @@ function baseUrlIncludesApiVersion(baseUrl) {
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
function resolveApiKey(options) {
|
|
303
|
-
const apiKey = options?.apiKey?.trim()
|
|
303
|
+
const apiKey = options?.apiKey?.trim();
|
|
304
304
|
if (!apiKey || apiKey === GCP_VERTEX_CREDENTIALS_MARKER || isPlaceholderApiKey(apiKey)) {
|
|
305
305
|
return undefined;
|
|
306
306
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { GoogleGenAI, } from "@google/genai";
|
|
2
|
-
import { getEnvApiKey } from "../env-api-keys.js";
|
|
3
2
|
import { calculateCost, clampThinkingLevel } from "../models.js";
|
|
4
3
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
5
4
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
@@ -28,7 +27,10 @@ export const streamGoogle = (model, context, options) => {
|
|
|
28
27
|
timestamp: Date.now(),
|
|
29
28
|
};
|
|
30
29
|
try {
|
|
31
|
-
const apiKey = options?.apiKey
|
|
30
|
+
const apiKey = options?.apiKey;
|
|
31
|
+
if (!apiKey) {
|
|
32
|
+
throw new Error(`No API key for provider: ${model.provider}`);
|
|
33
|
+
}
|
|
32
34
|
const client = createClient(model, apiKey, options?.headers);
|
|
33
35
|
let params = buildParams(model, context, options);
|
|
34
36
|
const nextParams = await options?.onPayload?.(params, model);
|
|
@@ -214,7 +216,7 @@ export const streamGoogle = (model, context, options) => {
|
|
|
214
216
|
return stream;
|
|
215
217
|
};
|
|
216
218
|
export const streamSimpleGoogle = (model, context, options) => {
|
|
217
|
-
const apiKey = options?.apiKey
|
|
219
|
+
const apiKey = options?.apiKey;
|
|
218
220
|
if (!apiKey) {
|
|
219
221
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
220
222
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
-
import { getEnvApiKey } from "../../env-api-keys.js";
|
|
3
2
|
import { headersToRecord } from "../../utils/headers.js";
|
|
4
3
|
import { sanitizeSurrogates } from "../../utils/sanitize-unicode.js";
|
|
5
4
|
export const generateImagesOpenRouter = async (model, context, options) => {
|
|
@@ -12,9 +11,9 @@ export const generateImagesOpenRouter = async (model, context, options) => {
|
|
|
12
11
|
timestamp: Date.now(),
|
|
13
12
|
};
|
|
14
13
|
try {
|
|
15
|
-
const apiKey = options?.apiKey
|
|
14
|
+
const apiKey = options?.apiKey;
|
|
16
15
|
if (!apiKey) {
|
|
17
|
-
throw new Error(`No API key
|
|
16
|
+
throw new Error(`No API key for provider: ${model.provider}`);
|
|
18
17
|
}
|
|
19
18
|
const client = createClient(model, apiKey, options?.headers);
|
|
20
19
|
let params = buildParams(model, context);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Mistral } from "@mistralai/mistralai";
|
|
2
|
-
import { getEnvApiKey } from "../env-api-keys.js";
|
|
3
2
|
import { calculateCost, clampThinkingLevel } from "../models.js";
|
|
4
3
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
5
4
|
import { shortHash } from "../utils/hash.js";
|
|
@@ -17,7 +16,7 @@ export const streamMistral = (model, context, options) => {
|
|
|
17
16
|
(async () => {
|
|
18
17
|
const output = createOutput(model);
|
|
19
18
|
try {
|
|
20
|
-
const apiKey = options?.apiKey
|
|
19
|
+
const apiKey = options?.apiKey;
|
|
21
20
|
if (!apiKey) {
|
|
22
21
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
23
22
|
}
|
|
@@ -62,7 +61,7 @@ export const streamMistral = (model, context, options) => {
|
|
|
62
61
|
* Maps provider-agnostic `SimpleStreamOptions` to Mistral options.
|
|
63
62
|
*/
|
|
64
63
|
export const streamSimpleMistral = (model, context, options) => {
|
|
65
|
-
const apiKey = options?.apiKey
|
|
64
|
+
const apiKey = options?.apiKey;
|
|
66
65
|
if (!apiKey) {
|
|
67
66
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
68
67
|
}
|