@bastani/pi-ai 0.9.25-alpha.1 → 0.9.25-alpha.3
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 +13 -0
- package/NOTICE.md +1 -1
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +78 -16
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/constrained-sampling.d.ts +1 -0
- package/dist/api/constrained-sampling.d.ts.map +1 -1
- package/dist/api/constrained-sampling.js +13 -5
- package/dist/api/constrained-sampling.js.map +1 -1
- package/dist/auth/oauth/anthropic.d.ts.map +1 -1
- package/dist/auth/oauth/anthropic.js +51 -1
- package/dist/auth/oauth/anthropic.js.map +1 -1
- package/dist/env-api-keys.d.ts +5 -0
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +5 -0
- package/dist/env-api-keys.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +20 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +2 -1
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/provider-retry.js +3 -2
- package/dist/utils/provider-retry.js.map +1 -1
- package/dist/utils/validation.d.ts +1 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +3 -0
- package/dist/utils/validation.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ This package is a Bastani fork of `@earendil-works/pi-ai`. Upstream history at t
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.25-alpha.3] - 2026-09-30
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added Anthropic workload identity federation from the Anthropic SDK environment variables `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, and `ANTHROPIC_IDENTITY_TOKEN_FILE` (plus optional `ANTHROPIC_SERVICE_ACCOUNT_ID` and `ANTHROPIC_WORKSPACE_ID`). API keys and `ANTHROPIC_AUTH_TOKEN` take precedence ([#10177](https://github.com/earendil-works/pi/issues/10177), [#10242](https://github.com/earendil-works/pi/pull/10242) by [@philfreo](https://github.com/philfreo)).
|
|
12
|
+
- Anthropic subscription login now offers a copy-code method for remote or headless machines, alongside the default browser callback method ([#10194](https://github.com/earendil-works/pi/pull/10194) by [@lucasmeijer](https://github.com/lucasmeijer)).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Fixed context overflow detection for Z.AI CN endpoint `Prompt exceeds max length` errors ([#10208](https://github.com/earendil-works/pi/issues/10208)).
|
|
17
|
+
- Anthropic `strict: "prefer"` tools now fall back to non-strict mode for unsupported string formats and additional array/object constraints instead of rejecting the request. Supported string length and pattern constraints remain strict ([#9953](https://github.com/earendil-works/pi/issues/9953)).
|
|
18
|
+
- Provider retries now use exponential backoff when `Retry-After` or `retry-after-ms` cannot be parsed as a finite delay ([#9571](https://github.com/earendil-works/pi/issues/9571)).
|
|
19
|
+
|
|
7
20
|
## [0.9.25-alpha.1] - 2026-09-29
|
|
8
21
|
|
|
9
22
|
### Added
|
package/NOTICE.md
CHANGED
|
@@ -6,7 +6,7 @@ monorepo at `packages/ai` and publishes at the same version as `@bastani/atomic`
|
|
|
6
6
|
|
|
7
7
|
- Upstream package: [`@earendil-works/pi-ai`](https://www.npmjs.com/package/@earendil-works/pi-ai)
|
|
8
8
|
- Original fork point: `v0.84.2` (`914cf1472e715297caa30db4b9535d534a9eb718`)
|
|
9
|
-
- Applicable Pi AI fixes
|
|
9
|
+
- Applicable Pi AI fixes synced through audited upstream `main`: `earendil-works/pi@7a11fe1c723b942fa093edb8b00f051ddb9b6627`, with remaining registry Pi packages at `0.99.2`. Atomic retains its adaptations and does not enable upstream virtual models.
|
|
10
10
|
- Catalog JSON under `src/providers/data/` is generated at build time from models.dev, matching upstream. It is not committed.
|
|
11
11
|
|
|
12
12
|
Original work is Copyright (c) 2025 Mario Zechner and is licensed under the MIT License.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAiBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAOb,MAAM,aAAa,CAAC;AAsKrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AAoPhE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAgSD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA6XzE,CAAC;AA4BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
1
|
+
import Anthropic, {} from "@anthropic-ai/sdk";
|
|
2
|
+
import { ANTHROPIC_FEDERATION_RULE_ID_ENV, ANTHROPIC_IDENTITY_TOKEN_FILE_ENV, ANTHROPIC_ORGANIZATION_ID_ENV, ANTHROPIC_SERVICE_ACCOUNT_ID_ENV, ANTHROPIC_WORKSPACE_ID_ENV, } from "../env-api-keys.js";
|
|
2
3
|
import { calculateCost } from "../models.js";
|
|
3
4
|
import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
|
|
4
5
|
import { assertSupportedDocumentMimeType } from "../utils/document-input.js";
|
|
@@ -340,16 +341,58 @@ function hasHeader(headers, name) {
|
|
|
340
341
|
}
|
|
341
342
|
return false;
|
|
342
343
|
}
|
|
343
|
-
function
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (hasHeader(headers, "authorization") ||
|
|
344
|
+
function hasRequestAuth(apiKey, headers) {
|
|
345
|
+
return (!!apiKey ||
|
|
346
|
+
hasHeader(headers, "authorization") ||
|
|
347
347
|
hasHeader(headers, "x-api-key") ||
|
|
348
|
-
hasHeader(headers, "cf-aig-authorization"))
|
|
349
|
-
|
|
348
|
+
hasHeader(headers, "cf-aig-authorization"));
|
|
349
|
+
}
|
|
350
|
+
function assertRequestAuth(provider, apiKey, headers) {
|
|
351
|
+
if (!hasRequestAuth(apiKey, headers))
|
|
352
|
+
throw new Error(`No API key for provider: ${provider}`);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Anthropic SDK client that never runs the SDK's own credential chain
|
|
356
|
+
* (ANTHROPIC_PROFILE config files, federation env vars). Without this, every
|
|
357
|
+
* client built with `apiKey: null, authToken: null` for header-owned auth would
|
|
358
|
+
* also resolve and exchange SDK credentials behind Atomic's auth resolver.
|
|
359
|
+
*/
|
|
360
|
+
class AtomicAnthropic extends Anthropic {
|
|
361
|
+
_shouldResolveDefaultCredentials() {
|
|
362
|
+
return false;
|
|
350
363
|
}
|
|
351
|
-
throw new Error(`No API key for provider: ${provider}`);
|
|
352
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* Workload identity federation config from the ANTHROPIC_* variables the
|
|
367
|
+
* Anthropic SDK documents; the SDK performs the token exchange and refresh.
|
|
368
|
+
* Only for the anthropic provider, since the exchange is an Anthropic API
|
|
369
|
+
* endpoint, and only when no key or auth header was resolved.
|
|
370
|
+
*/
|
|
371
|
+
function getAnthropicFederation(model, apiKey, headers, env) {
|
|
372
|
+
if (model.provider !== "anthropic" || hasRequestAuth(apiKey, headers))
|
|
373
|
+
return undefined;
|
|
374
|
+
const federationRuleId = getProviderEnvValue(ANTHROPIC_FEDERATION_RULE_ID_ENV, env);
|
|
375
|
+
const organizationId = getProviderEnvValue(ANTHROPIC_ORGANIZATION_ID_ENV, env);
|
|
376
|
+
const identityTokenFile = getProviderEnvValue(ANTHROPIC_IDENTITY_TOKEN_FILE_ENV, env);
|
|
377
|
+
if (!federationRuleId || !organizationId || !identityTokenFile)
|
|
378
|
+
return undefined;
|
|
379
|
+
return {
|
|
380
|
+
organization_id: organizationId,
|
|
381
|
+
workspace_id: getProviderEnvValue(ANTHROPIC_WORKSPACE_ID_ENV, env),
|
|
382
|
+
authentication: {
|
|
383
|
+
type: "oidc_federation",
|
|
384
|
+
federation_rule_id: federationRuleId,
|
|
385
|
+
service_account_id: getProviderEnvValue(ANTHROPIC_SERVICE_ACCOUNT_ID_ENV, env),
|
|
386
|
+
identity_token: { source: "file", path: identityTokenFile },
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* The SDK caches the federated access token per client, but a client is created
|
|
392
|
+
* per request. Keep one client for the current federation config and fetch, and
|
|
393
|
+
* clone it per request with `withOptions()`, which shares the token cache.
|
|
394
|
+
*/
|
|
395
|
+
let federationClient;
|
|
353
396
|
const ANTHROPIC_MESSAGE_EVENTS = new Set([
|
|
354
397
|
"message_start",
|
|
355
398
|
"message_delta",
|
|
@@ -547,7 +590,9 @@ export const stream = (model, context, options) => {
|
|
|
547
590
|
}
|
|
548
591
|
else {
|
|
549
592
|
const apiKey = options?.apiKey;
|
|
550
|
-
|
|
593
|
+
const federation = getAnthropicFederation(model, apiKey, options?.headers, options?.env);
|
|
594
|
+
if (!federation)
|
|
595
|
+
assertRequestAuth(model.provider, apiKey, options?.headers);
|
|
551
596
|
let copilotDynamicHeaders;
|
|
552
597
|
if (model.provider === "github-copilot") {
|
|
553
598
|
const hasImages = hasCopilotVisionInput(normalizedContext.messages);
|
|
@@ -562,7 +607,7 @@ export const stream = (model, context, options) => {
|
|
|
562
607
|
const created = createClient(model, apiKey, model.reasoning && options?.thinkingEnabled === true && (options.interleavedThinking ?? true), shouldUseFineGrainedToolStreamingBeta(model, context), shouldUseServerSideFallbackBeta(model), options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, getAnthropicCompat(model).supportsMidConvoToolChanges &&
|
|
563
608
|
getAnthropicCompat(model).supportsMidConvoSystemMessages &&
|
|
564
609
|
(getInitialSystemMessage(normalizedContext.messages)?.toolsAdded?.length ?? 0) > 0 &&
|
|
565
|
-
!hasToolRedefinitions(normalizedContext.messages));
|
|
610
|
+
!hasToolRedefinitions(normalizedContext.messages), federation);
|
|
566
611
|
client = created.client;
|
|
567
612
|
isOAuth = created.isOAuthToken;
|
|
568
613
|
}
|
|
@@ -881,7 +926,9 @@ function mapThinkingLevelToEffort(model, level) {
|
|
|
881
926
|
}
|
|
882
927
|
}
|
|
883
928
|
export const streamSimple = (model, context, options) => {
|
|
884
|
-
|
|
929
|
+
if (!getAnthropicFederation(model, options?.apiKey, options?.headers, options?.env)) {
|
|
930
|
+
assertRequestAuth(model.provider, options?.apiKey, options?.headers);
|
|
931
|
+
}
|
|
885
932
|
const base = {
|
|
886
933
|
...buildBaseOptions(model, context, options, options?.apiKey),
|
|
887
934
|
toolChoice: options?.toolChoice,
|
|
@@ -916,7 +963,7 @@ export const streamSimple = (model, context, options) => {
|
|
|
916
963
|
function isOAuthToken(apiKey) {
|
|
917
964
|
return apiKey.includes("sk-ant-oat");
|
|
918
965
|
}
|
|
919
|
-
function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStreamingBeta, useServerSideFallbackBeta, optionsHeaders, fetch, dynamicHeaders, sessionId, useNativeToolChanges = false) {
|
|
966
|
+
function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStreamingBeta, useServerSideFallbackBeta, optionsHeaders, fetch, dynamicHeaders, sessionId, useNativeToolChanges = false, federation) {
|
|
920
967
|
// Adaptive thinking models have interleaved thinking built in, so skip the beta header.
|
|
921
968
|
const needsInterleavedBeta = interleavedThinking && model.compat?.forceAdaptiveThinking !== true;
|
|
922
969
|
const betaFeatures = [];
|
|
@@ -942,7 +989,7 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
|
|
|
942
989
|
const uniqueBetaFeatures = [...new Set(betaFeatures)];
|
|
943
990
|
// Copilot: Bearer auth, selective betas.
|
|
944
991
|
if (model.provider === "github-copilot") {
|
|
945
|
-
const client = new
|
|
992
|
+
const client = new AtomicAnthropic({
|
|
946
993
|
apiKey: null,
|
|
947
994
|
authToken: apiKey ?? null,
|
|
948
995
|
baseURL: model.baseUrl,
|
|
@@ -958,7 +1005,7 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
|
|
|
958
1005
|
}
|
|
959
1006
|
// OAuth: Bearer auth, Claude Code identity headers
|
|
960
1007
|
if (apiKey && isOAuthToken(apiKey)) {
|
|
961
|
-
const client = new
|
|
1008
|
+
const client = new AtomicAnthropic({
|
|
962
1009
|
apiKey: null,
|
|
963
1010
|
authToken: apiKey,
|
|
964
1011
|
baseURL: model.baseUrl,
|
|
@@ -974,7 +1021,7 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
|
|
|
974
1021
|
});
|
|
975
1022
|
return { client, isOAuthToken: true };
|
|
976
1023
|
}
|
|
977
|
-
// API key
|
|
1024
|
+
// API key, header-owned auth, or workload identity federation.
|
|
978
1025
|
const compat = getAnthropicCompat(model);
|
|
979
1026
|
const sessionAffinityHeaders = {};
|
|
980
1027
|
if (sessionId && compat.sendSessionAffinityHeaders) {
|
|
@@ -986,7 +1033,22 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
|
|
|
986
1033
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
987
1034
|
...(uniqueBetaFeatures.length > 0 ? { "anthropic-beta": uniqueBetaFeatures.join(",") } : {}),
|
|
988
1035
|
}, sessionAffinityHeaders, model.headers, optionsHeaders);
|
|
989
|
-
|
|
1036
|
+
if (federation) {
|
|
1037
|
+
const key = JSON.stringify([model.baseUrl, federation]);
|
|
1038
|
+
if (federationClient?.key !== key || federationClient.fetch !== fetch) {
|
|
1039
|
+
const client = new AtomicAnthropic({
|
|
1040
|
+
apiKey: null,
|
|
1041
|
+
authToken: null,
|
|
1042
|
+
config: federation,
|
|
1043
|
+
baseURL: model.baseUrl,
|
|
1044
|
+
dangerouslyAllowBrowser: true,
|
|
1045
|
+
fetch,
|
|
1046
|
+
});
|
|
1047
|
+
federationClient = { key, fetch, client };
|
|
1048
|
+
}
|
|
1049
|
+
return { client: federationClient.client.withOptions({ defaultHeaders }), isOAuthToken: false };
|
|
1050
|
+
}
|
|
1051
|
+
const client = new AtomicAnthropic({
|
|
990
1052
|
apiKey: apiKey ?? null,
|
|
991
1053
|
authToken: null,
|
|
992
1054
|
baseURL: model.baseUrl,
|