@oh-my-pi/pi-ai 17.2.4 → 17.2.6
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 +25 -0
- package/dist/types/dialect/examples.d.ts +18 -2
- package/dist/types/dialect/inventory.d.ts +6 -9
- package/dist/types/dialect/rendering.d.ts +9 -0
- package/dist/types/dialect/types.d.ts +0 -1
- package/dist/types/error/aws.d.ts +5 -1
- package/dist/types/error/flags.d.ts +5 -0
- package/dist/types/providers/aws-credentials.d.ts +5 -10
- package/dist/types/providers/bedrock-mantle.d.ts +13 -0
- package/dist/types/providers/openai-shared.d.ts +17 -5
- package/dist/types/providers/transform-messages.d.ts +1 -1
- package/dist/types/registry/amazon-bedrock.d.ts +7 -1
- package/dist/types/registry/aws.d.ts +13 -0
- package/dist/types/registry/bedrock-mantle.d.ts +22 -0
- package/dist/types/registry/registry.d.ts +25 -1
- package/dist/types/registry/types.d.ts +24 -0
- package/dist/types/types.d.ts +11 -4
- package/dist/types/utils/aws-profile.d.ts +17 -0
- package/dist/types/utils/harmony-leak.d.ts +9 -0
- package/dist/types/utils/schema/typescript.d.ts +8 -2
- package/package.json +4 -4
- package/src/auth-broker/discover.ts +2 -1
- package/src/auth-broker/wire-schema-resource.ts +6 -1
- package/src/auth-storage.ts +2 -2
- package/src/dialect/examples.ts +50 -12
- package/src/dialect/gemini.ts +17 -31
- package/src/dialect/harmony.ts +1 -2
- package/src/dialect/inventory.ts +21 -64
- package/src/dialect/rendering.ts +54 -0
- package/src/dialect/types.ts +0 -1
- package/src/error/aws.ts +5 -1
- package/src/error/flags.ts +8 -0
- package/src/providers/amazon-bedrock.ts +5 -5
- package/src/providers/anthropic.ts +77 -32
- package/src/providers/aws-credentials.ts +262 -76
- package/src/providers/bedrock-mantle.ts +110 -0
- package/src/providers/cursor.ts +6 -3
- package/src/providers/ollama.ts +27 -1
- package/src/providers/openai-codex-responses.ts +19 -5
- package/src/providers/openai-shared.ts +84 -35
- package/src/providers/transform-messages.ts +1 -1
- package/src/registry/amazon-bedrock.ts +9 -14
- package/src/registry/aws.ts +57 -0
- package/src/registry/bedrock-mantle.ts +34 -0
- package/src/registry/google-vertex.ts +2 -2
- package/src/registry/registry.ts +2 -0
- package/src/registry/types.ts +30 -0
- package/src/stream.ts +46 -30
- package/src/types.ts +14 -8
- package/src/utils/aws-profile.ts +88 -0
- package/src/utils/harmony-leak.ts +12 -0
- package/src/utils/schema/typescript.ts +21 -7
- package/src/utils.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.2.6] - 2026-08-03
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added profile-aware Bedrock Mantle region selection, authenticated model discovery, bearer-token or SigV4 authentication, and credential refresh handling for OpenAI Responses models.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed an issue where Ollama requests without a user-role message would fail to generate output or silently fail with a misleading error.
|
|
14
|
+
|
|
15
|
+
## [17.2.5] - 2026-08-03
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Standardized tool-call examples in `renderToolExamples` and `renderToolInventory` to use Python keyword-argument syntax (`name(key="value")`) across all models, removing the model-specific dialect parameter and the `DialectRenderOptions.example` flag.
|
|
20
|
+
- Updated `renderToolInventory` to render the tool catalog as a unified OpenAI-Harmony-style `## functions` block using TypeScript type declarations and comments, replacing the previous per-tool Markdown sections.
|
|
21
|
+
- Added a `style: "harmony"` option to `jsonSchemaToTypeScript` for generating compact, comma-delimited TypeScript definitions.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Fixed a session-blocking issue where unescaped Harmony control tokens in replayed assistant responses and tool inputs caused subsequent requests to be rejected with `invalid_prompt` errors.
|
|
26
|
+
- Fixed an issue where Codex Responses dropped native image-generation results from assistant content and replays due to stale `generating` statuses.
|
|
27
|
+
- Fixed Anthropic stream truncation handling where unexpected connection closures were incorrectly treated as clean stops, causing the agent loop to halt silently mid-sentence.
|
|
28
|
+
- Optimized Anthropic prompt caching to prevent unnecessary cache invalidation of the entire system prefix when volatile project footer details (such as current working directory, date, or workspace tree) change.
|
|
29
|
+
|
|
5
30
|
## [17.2.4] - 2026-08-01
|
|
6
31
|
|
|
7
32
|
### Fixed
|
|
@@ -1,2 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { InbandTool } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Render a tool's examples as an `<examples>` block. Calls render in Python
|
|
4
|
+
* keyword-argument syntax (`name(key="value", n=1)`) regardless of the model's
|
|
5
|
+
* tool-call dialect, so example bytes stay identical across models. Multiline
|
|
6
|
+
* string args render as verbatim `"""…"""` blocks, and a call whose only
|
|
7
|
+
* argument is a string renders as the bare value — the block already names the
|
|
8
|
+
* tool, and payload args (commands, code, patches) read best verbatim.
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderToolExamples(tool: InbandTool, intentField?: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Render a tool's examples as JSDoc-style `@example` lines for comment-gutter
|
|
13
|
+
* contexts (the Harmony `namespace functions` inventory): `@example "caption"`
|
|
14
|
+
* followed by the call in the same Python kwargs syntax as the wire block. The
|
|
15
|
+
* tag line delimits each example, so no XML envelope is needed — which is why
|
|
16
|
+
* the inventory uses this instead of `//`-prefixing the `<examples>` block.
|
|
17
|
+
*/
|
|
18
|
+
export declare function renderToolExamplesJsdoc(tool: InbandTool): string;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { InbandTool } from "./types.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `model` is a model id; the native example dialect is resolved from it
|
|
10
|
-
* (`preferredDialect`, which falls back to XML for empty/unknown ids).
|
|
3
|
+
* Tool catalog in the OpenAI-Harmony `namespace functions { … }` shape: each
|
|
4
|
+
* tool renders as its full description (and Python-syntax examples) as `//`
|
|
5
|
+
* comment lines above a flat `type <name> = (_: {…});` declaration. Shared by
|
|
6
|
+
* the verbose system-prompt inventory and `/dump` so both render the catalog
|
|
7
|
+
* the same way.
|
|
11
8
|
*/
|
|
12
|
-
export declare function renderToolInventory(tools: readonly InbandTool[]
|
|
9
|
+
export declare function renderToolInventory(tools: readonly InbandTool[]): string;
|
|
@@ -4,6 +4,15 @@ export declare function renderToolResponseResults(results: readonly DialectToolR
|
|
|
4
4
|
export declare function kimiCallId(name: string, id: string, index: number): string;
|
|
5
5
|
export declare function harmonyRecipient(name: string): string;
|
|
6
6
|
export declare function stringifyJson(value: unknown): string;
|
|
7
|
+
/**
|
|
8
|
+
* Render `name(key=value, …)` with Python-literal argument values. Top-level
|
|
9
|
+
* multiline strings render as verbatim `"""…"""` blocks so payload-carrying
|
|
10
|
+
* args (file content, scripts, patches) keep real newlines instead of `\n`
|
|
11
|
+
* escape soup; nested values always use escaped single-line literals.
|
|
12
|
+
*/
|
|
13
|
+
export declare function pyCall(name: string, args: Record<string, unknown>): string;
|
|
14
|
+
/** Render a JSON-ish value as a Python literal (`True`/`False`/`None`, escaped strings, lists, dicts). */
|
|
15
|
+
export declare function pyValue(value: unknown): string;
|
|
7
16
|
export declare function escapeXmlAttr(value: string): string;
|
|
8
17
|
export declare function escapeXmlText(value: string): string;
|
|
9
18
|
export type AssistantTranscriptParts = {
|
|
@@ -9,7 +9,11 @@ export type AwsCredentialsErrorKind =
|
|
|
9
9
|
/** SSO `GetRoleCredentials` call failed or returned no role. */
|
|
10
10
|
| "sso-role"
|
|
11
11
|
/** External `credential_process` failed, timed out, or emitted bad output. */
|
|
12
|
-
| "credential-process"
|
|
12
|
+
| "credential-process"
|
|
13
|
+
/** STS web-identity exchange failed or returned malformed credentials. */
|
|
14
|
+
| "web-identity"
|
|
15
|
+
/** ECS/container credential endpoint failed or returned malformed credentials. */
|
|
16
|
+
| "container";
|
|
13
17
|
/** A failure resolving AWS credentials for the Bedrock provider. */
|
|
14
18
|
export declare class AwsCredentialsError extends Error {
|
|
15
19
|
readonly kind: AwsCredentialsErrorKind;
|
|
@@ -40,6 +40,11 @@ export declare function retriable(id: number | undefined, opts?: {
|
|
|
40
40
|
}): boolean;
|
|
41
41
|
export declare function status(error: unknown): number | undefined;
|
|
42
42
|
export declare function isStreamReadErrorText(text: string): boolean;
|
|
43
|
+
/** Persisted-text form of {@link isStreamEnvelopeError}: recognizes the
|
|
44
|
+
* prefix-tagged envelope diagnostic on an aborted turn's `errorMessage` /
|
|
45
|
+
* `stopDetails.explanation` so loop-level salvage can classify it after the
|
|
46
|
+
* original `Error` instance is gone. */
|
|
47
|
+
export declare function isStreamEnvelopeErrorText(text: string): boolean;
|
|
43
48
|
export declare function classify(error: unknown, api?: Api): number;
|
|
44
49
|
/**
|
|
45
50
|
* Whether an error (or message string) classifies as an account usage/quota
|
|
@@ -4,16 +4,11 @@
|
|
|
4
4
|
* Chain (first hit wins):
|
|
5
5
|
* 1. Static credentials from the environment
|
|
6
6
|
* (`AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` [+ `AWS_SESSION_TOKEN`]).
|
|
7
|
-
* 2.
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - `credential_process` — an external command emitting the AWS SDK
|
|
13
|
-
* `Version: 1` JSON envelope on stdout. Used by `aws-vault`, `granted`,
|
|
14
|
-
* in-house brokers, etc.
|
|
15
|
-
* 3. EC2 IMDSv2 (only when `AWS_EC2_METADATA_DISABLED` is unset / falsey and
|
|
16
|
-
* `169.254.169.254` is reachable within a 1 s timeout).
|
|
7
|
+
* 2. Web identity (`AWS_WEB_IDENTITY_TOKEN_FILE` + `AWS_ROLE_ARN`).
|
|
8
|
+
* 3. Profile in `~/.aws/credentials` (and `~/.aws/config` for SSO):
|
|
9
|
+
* - static keys, SSO, or `credential_process`.
|
|
10
|
+
* 4. ECS/container credentials from `AWS_CONTAINER_CREDENTIALS_*`.
|
|
11
|
+
* 5. EC2 IMDSv2 when metadata is enabled.
|
|
17
12
|
*
|
|
18
13
|
* Resolved credentials are cached process-wide per profile and refreshed
|
|
19
14
|
* 60 s before `Expiration` to absorb clock skew.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type AwsBedrockProviderOptions } from "../registry/aws.js";
|
|
2
|
+
import type { FetchImpl, Model } from "../types.js";
|
|
3
|
+
import type { OpenAIResponsesOptions } from "./openai-responses.js";
|
|
4
|
+
export type BedrockMantleProviderOptions = AwsBedrockProviderOptions;
|
|
5
|
+
export interface BedrockMantleOptions extends OpenAIResponsesOptions {
|
|
6
|
+
providerOptions?: BedrockMantleProviderOptions;
|
|
7
|
+
}
|
|
8
|
+
export declare function createBedrockMantleAuthenticatedFetch(options?: BedrockMantleOptions): FetchImpl;
|
|
9
|
+
export interface PreparedBedrockMantleRequest {
|
|
10
|
+
model: Model<"openai-responses">;
|
|
11
|
+
options: OpenAIResponsesOptions;
|
|
12
|
+
}
|
|
13
|
+
export declare function prepareBedrockMantleRequest(model: Model<"openai-responses">, options: BedrockMantleOptions): PreparedBedrockMantleRequest;
|
|
@@ -421,17 +421,27 @@ export interface BuildResponsesInputOptions<TApi extends Api> {
|
|
|
421
421
|
preserveAssistantMessageIds?: boolean;
|
|
422
422
|
}
|
|
423
423
|
/**
|
|
424
|
-
* Escape reserved Harmony control tokens in the
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
424
|
+
* Escape reserved Harmony control tokens in the free-text fields of replayed
|
|
425
|
+
* Responses input items: user/developer/system text, tool-result output,
|
|
426
|
+
* assistant message text, and tool-call payloads.
|
|
427
|
+
*
|
|
428
|
+
* Tool-call items are covered deliberately. The original #6913 fix skipped
|
|
429
|
+
* model-owned items on the theory that they carry no client data — but a model
|
|
430
|
+
* legitimately writing *about* Harmony samples `<|channel|>` etc. into its own
|
|
431
|
+
* `function_call.arguments`, and a full-transcript replay (stale or blocked
|
|
432
|
+
* previous_response_id, provider fallback) feeds those bytes back as input,
|
|
433
|
+
* which gpt-5.x reject with invalid_prompt / "Request blocked", permanently
|
|
434
|
+
* poisoning the session. `arguments` is a JSON document, so it uses
|
|
435
|
+
* {@link escapeHarmonyControlTokensInJson} to stay parseable. Reasoning items
|
|
436
|
+
* are left untouched: `encrypted_content` is opaque and plaintext summaries
|
|
437
|
+
* are never rendered back into the prompt.
|
|
428
438
|
*
|
|
429
439
|
* Native history replay pushes stored `providerPayload` items straight onto the
|
|
430
440
|
* wire, bypassing {@link convertResponsesInputContent}; without this a stored
|
|
431
441
|
* `input_text` carrying `<|channel|>analysis` still reaches gpt-5.x raw (#6913).
|
|
432
442
|
* Callers gate on {@link isHarmonyDialectModel}. Items are copied, not mutated.
|
|
433
443
|
*/
|
|
434
|
-
export declare function
|
|
444
|
+
export declare function escapeReplayedControlTokens(items: ResponseInput): ResponseInput;
|
|
435
445
|
export declare function buildResponsesInput<TApi extends Api>(options: BuildResponsesInputOptions<TApi>): ResponseInput;
|
|
436
446
|
export declare function convertResponsesAssistantMessage<TApi extends Api>(assistantMsg: AssistantMessage, model: Model<TApi>, msgIndex: number, knownCallIds: Set<string>, includeThinkingSignatures?: boolean, customCallIds?: Set<string>, preserveMessageIds?: boolean, supportsCustomToolCalls?: boolean, customToolWireNameMap?: ReadonlyMap<string, string>, computerCallIds?: Set<string>): ResponseInput;
|
|
437
447
|
/** Appends one tool result while keeping consecutive outputs ahead of its synthetic image messages. */
|
|
@@ -516,6 +526,8 @@ export interface ProcessResponsesStreamOptions {
|
|
|
516
526
|
requestServiceTier?: ServiceTier;
|
|
517
527
|
}
|
|
518
528
|
export declare function computerCallMetadata(item: ResponseComputerToolCall): ComputerToolCallMetadata;
|
|
529
|
+
/** Append a native Responses image result and emit its completion event. */
|
|
530
|
+
export declare function appendResponsesImageResult(output: AssistantMessage, stream: AssistantMessageEventStream, result: string): void;
|
|
519
531
|
export declare function processResponsesStream<TApi extends Api>(openaiStream: AsyncIterable<ResponseStreamEvent>, output: AssistantMessage, stream: AssistantMessageEventStream, model: Model<TApi>, options?: ProcessResponsesStreamOptions): Promise<void>;
|
|
520
532
|
export declare function mapOpenAIResponsesStopReason(status: ResponseStatus | undefined): StopReason;
|
|
521
533
|
export declare function hasExecutableIncompleteResponsesToolCalls(output: AssistantMessage): boolean;
|
|
@@ -13,7 +13,7 @@ import type { Api, AssistantMessage, Message, Model } from "../types.js";
|
|
|
13
13
|
* credential redaction is enabled. Exported so hosts can route the same shapes
|
|
14
14
|
* through reversible obfuscation (keyed placeholders restored before local tool
|
|
15
15
|
* execution) instead of the irreversible `[*_token_redacted]` rewrite below —
|
|
16
|
-
* an irreversible placeholder echoed back in edit-tool `
|
|
16
|
+
* an irreversible placeholder echoed back in edit-tool `old_string` can never
|
|
17
17
|
* match the real bytes on disk.
|
|
18
18
|
*/
|
|
19
19
|
export declare const SENSITIVE_TOKEN_RE: RegExp;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { resolveAwsRegistryApiKey } from "./aws.js";
|
|
1
2
|
export declare const amazonBedrockProvider: {
|
|
2
3
|
readonly id: "amazon-bedrock";
|
|
3
4
|
readonly name: "Amazon Bedrock";
|
|
4
|
-
readonly envKeys:
|
|
5
|
+
readonly envKeys: typeof resolveAwsRegistryApiKey;
|
|
6
|
+
readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
|
|
7
|
+
region: string | undefined;
|
|
8
|
+
profile: string | undefined;
|
|
9
|
+
bearerToken: string | undefined;
|
|
10
|
+
};
|
|
5
11
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface AwsBedrockProviderOptions extends Readonly<Record<string, unknown>> {
|
|
2
|
+
/** AWS region used in the service endpoint and SigV4 credential scope. */
|
|
3
|
+
region?: string;
|
|
4
|
+
/** Named AWS shared-credentials/config profile. */
|
|
5
|
+
profile?: string;
|
|
6
|
+
/** Amazon Bedrock API key sent as a bearer token, ahead of SigV4 credential resolution. */
|
|
7
|
+
bearerToken?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function hasAwsCredentialSource(): boolean;
|
|
10
|
+
/** Registry key marker for AWS transports that resolve their own bearer/IAM credentials. */
|
|
11
|
+
export declare function resolveAwsRegistryApiKey(): string | undefined;
|
|
12
|
+
/** Resolve a real AWS bearer token while filtering the registry's auth marker. */
|
|
13
|
+
export declare function resolveAwsBearerToken(apiKey?: string, bearerToken?: string): string | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Model } from "../types.js";
|
|
2
|
+
import { resolveAwsRegistryApiKey } from "./aws.js";
|
|
3
|
+
export declare const bedrockMantleProvider: {
|
|
4
|
+
readonly id: "bedrock-mantle";
|
|
5
|
+
readonly name: "Amazon Bedrock Mantle";
|
|
6
|
+
readonly envKeys: typeof resolveAwsRegistryApiKey;
|
|
7
|
+
readonly allowsMissingApiKey: true;
|
|
8
|
+
readonly prepareRequest: (model: Model<import("@oh-my-pi/pi-catalog").Api>, options: import("../index.js").StreamOptions) => import("../providers/bedrock-mantle.js").PreparedBedrockMantleRequest;
|
|
9
|
+
readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
|
|
10
|
+
providerOptions: Readonly<Record<string, unknown>> | undefined;
|
|
11
|
+
};
|
|
12
|
+
readonly prepareModelDiscovery: (config: import("./types.js").ProviderModelDiscoveryConfig) => {
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
fetch?: import("@oh-my-pi/pi-utils").FetchImpl;
|
|
15
|
+
apiKey: undefined;
|
|
16
|
+
authenticated: false;
|
|
17
|
+
} | {
|
|
18
|
+
authenticated: true;
|
|
19
|
+
baseUrl: string;
|
|
20
|
+
fetch: import("@oh-my-pi/pi-utils").FetchImpl;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -25,7 +25,12 @@ declare const ALL: ({
|
|
|
25
25
|
} | {
|
|
26
26
|
readonly id: "amazon-bedrock";
|
|
27
27
|
readonly name: "Amazon Bedrock";
|
|
28
|
-
readonly envKeys: (
|
|
28
|
+
readonly envKeys: typeof import("./aws.js").resolveAwsRegistryApiKey;
|
|
29
|
+
readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
|
|
30
|
+
region: string | undefined;
|
|
31
|
+
profile: string | undefined;
|
|
32
|
+
bearerToken: string | undefined;
|
|
33
|
+
};
|
|
29
34
|
} | {
|
|
30
35
|
readonly id: "anthropic";
|
|
31
36
|
readonly name: "Anthropic (Claude Pro/Max)";
|
|
@@ -41,6 +46,25 @@ declare const ALL: ({
|
|
|
41
46
|
readonly id: "baseten";
|
|
42
47
|
readonly name: "Baseten";
|
|
43
48
|
readonly login: (cb: import("./oauth/index.js").OAuthLoginCallbacks) => Promise<string>;
|
|
49
|
+
} | {
|
|
50
|
+
readonly id: "bedrock-mantle";
|
|
51
|
+
readonly name: "Amazon Bedrock Mantle";
|
|
52
|
+
readonly envKeys: typeof import("./aws.js").resolveAwsRegistryApiKey;
|
|
53
|
+
readonly allowsMissingApiKey: true;
|
|
54
|
+
readonly prepareRequest: (model: import("@oh-my-pi/pi-catalog").Model<import("@oh-my-pi/pi-catalog").Api>, options: import("../index.js").StreamOptions) => import("../providers/bedrock-mantle.js").PreparedBedrockMantleRequest;
|
|
55
|
+
readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
|
|
56
|
+
providerOptions: Readonly<Record<string, unknown>> | undefined;
|
|
57
|
+
};
|
|
58
|
+
readonly prepareModelDiscovery: (config: import("./types.js").ProviderModelDiscoveryConfig) => {
|
|
59
|
+
baseUrl?: string;
|
|
60
|
+
fetch?: import("@oh-my-pi/pi-utils").FetchImpl;
|
|
61
|
+
apiKey: undefined;
|
|
62
|
+
authenticated: false;
|
|
63
|
+
} | {
|
|
64
|
+
authenticated: true;
|
|
65
|
+
baseUrl: string;
|
|
66
|
+
fetch: import("@oh-my-pi/pi-utils").FetchImpl;
|
|
67
|
+
};
|
|
44
68
|
} | {
|
|
45
69
|
readonly id: "cerebras";
|
|
46
70
|
readonly name: "Cerebras";
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* (default model, model-manager factory, catalog discovery) lives in
|
|
10
10
|
* `@oh-my-pi/pi-catalog`'s descriptor table.
|
|
11
11
|
*/
|
|
12
|
+
import type { Api, FetchImpl, Model, SimpleStreamOptions, StreamOptions } from "../types.js";
|
|
12
13
|
import type { OAuthCredentials, OAuthLoginCallbacks } from "./oauth/types.js";
|
|
13
14
|
/**
|
|
14
15
|
* API-key environment fallback: either a single env var name (e.g.
|
|
@@ -16,6 +17,21 @@ import type { OAuthCredentials, OAuthLoginCallbacks } from "./oauth/types.js";
|
|
|
16
17
|
* the host (Vertex ADC, Bedrock credential chains, …).
|
|
17
18
|
*/
|
|
18
19
|
export type KeyResolver = string | (() => string | undefined);
|
|
20
|
+
/** Credentials are resolved by the provider transport rather than used as a bearer string. */
|
|
21
|
+
export declare const AUTHENTICATED_SENTINEL = "<authenticated>";
|
|
22
|
+
export interface PreparedProviderRequest {
|
|
23
|
+
readonly model: Model<Api>;
|
|
24
|
+
readonly options: StreamOptions;
|
|
25
|
+
}
|
|
26
|
+
export type ProviderRequestPreparer = (model: Model<Api>, options: StreamOptions) => PreparedProviderRequest;
|
|
27
|
+
export type ProviderSimpleOptionsMapper = (options: SimpleStreamOptions) => Readonly<Record<string, unknown>>;
|
|
28
|
+
export interface ProviderModelDiscoveryConfig {
|
|
29
|
+
readonly apiKey?: string;
|
|
30
|
+
readonly baseUrl?: string;
|
|
31
|
+
readonly fetch?: FetchImpl;
|
|
32
|
+
readonly authenticated?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type ProviderModelDiscoveryPreparer = (config: ProviderModelDiscoveryConfig) => ProviderModelDiscoveryConfig;
|
|
19
35
|
/**
|
|
20
36
|
* Declarative description of a single provider's auth/login wiring. All
|
|
21
37
|
* fields are optional except `id`/`name`; presence of a field opts the
|
|
@@ -39,6 +55,14 @@ export interface ProviderDefinition {
|
|
|
39
55
|
/** Whether to surface in the interactive login list. Defaults to true when `login` is present. */
|
|
40
56
|
readonly showInLoginList?: boolean;
|
|
41
57
|
readonly envKeys?: KeyResolver;
|
|
58
|
+
/** Provider transport can authenticate without a resolved API-key string. */
|
|
59
|
+
readonly allowsMissingApiKey?: boolean;
|
|
60
|
+
/** Provider-owned request shaping applied before generic API dispatch. */
|
|
61
|
+
readonly prepareRequest?: ProviderRequestPreparer;
|
|
62
|
+
/** Provider-owned projection from the generic simple-stream option bag. */
|
|
63
|
+
readonly mapSimpleOptions?: ProviderSimpleOptionsMapper;
|
|
64
|
+
/** Provider-owned authentication and endpoint setup for model discovery. */
|
|
65
|
+
readonly prepareModelDiscovery?: ProviderModelDiscoveryPreparer;
|
|
42
66
|
readonly login?: (callbacks: OAuthLoginCallbacks) => Promise<OAuthCredentials | string>;
|
|
43
67
|
readonly refreshToken?: (credentials: OAuthCredentials) => Promise<OAuthCredentials>;
|
|
44
68
|
readonly getApiKey?: (credentials: OAuthCredentials) => string;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -124,10 +124,10 @@ export declare function serviceTierFamily(model: ServiceTierModel): ServiceTierF
|
|
|
124
124
|
export declare function resolveModelServiceTier(tiers: ServiceTierByFamily | null | undefined, model: Pick<Model, "provider" | "api" | "id">): ServiceTier | undefined;
|
|
125
125
|
/**
|
|
126
126
|
* True when the tier should be sent on the wire as the provider's service-tier
|
|
127
|
-
* request field. OpenAI / OpenAI-Codex accept
|
|
128
|
-
* (Gemini API + Vertex) and OpenRouter accept `flex`/`priority`;
|
|
129
|
-
* Serverless realizes only its Priority serving path. Anthropic is
|
|
130
|
-
* realizes `priority` via `speed: "fast"
|
|
127
|
+
* request field. OpenAI / OpenAI-Codex accept every {@link ServiceTier};
|
|
128
|
+
* Google (Gemini API + Vertex) and OpenRouter accept `flex`/`priority`;
|
|
129
|
+
* Fireworks Serverless realizes only its Priority serving path. Anthropic is
|
|
130
|
+
* absent because it realizes `priority` via `speed: "fast"`.
|
|
131
131
|
*/
|
|
132
132
|
export declare function shouldSendServiceTier(serviceTier: ServiceTier | null | undefined, target: Provider | ServiceTierModel | undefined): boolean;
|
|
133
133
|
/**
|
|
@@ -249,6 +249,11 @@ export interface StreamOptions {
|
|
|
249
249
|
* For example, Anthropic uses `user_id` for abuse tracking and rate limiting.
|
|
250
250
|
*/
|
|
251
251
|
metadata?: Record<string, unknown>;
|
|
252
|
+
/**
|
|
253
|
+
* Provider-owned request configuration. Provider hooks interpret this bag;
|
|
254
|
+
* generic API transports do not forward its fields onto the wire.
|
|
255
|
+
*/
|
|
256
|
+
providerOptions?: Readonly<Record<string, unknown>>;
|
|
252
257
|
/** OpenAI Responses/Codex response fields to include verbatim. */
|
|
253
258
|
include?: OpenAIResponseInclude[];
|
|
254
259
|
/**
|
|
@@ -660,6 +665,8 @@ export interface AssistantRetryRecovery {
|
|
|
660
665
|
export interface ContextSnapshot {
|
|
661
666
|
promptTokens: number;
|
|
662
667
|
nonMessageTokens: number;
|
|
668
|
+
/** Estimated prompt tokens removed by local history rewrites after this provider snapshot was recorded. */
|
|
669
|
+
historyRewriteTokensRemoved?: number;
|
|
663
670
|
lastMessageTimestamp?: number;
|
|
664
671
|
}
|
|
665
672
|
export interface AssistantMessage {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** INI sections with `profile ` / `sso-session ` prefixes normalized. */
|
|
2
|
+
export type AwsIniFile = Record<string, Record<string, string>>;
|
|
3
|
+
export declare function parseAwsIni(text: string): AwsIniFile;
|
|
4
|
+
/** Resolve the selected shared-credentials profile. */
|
|
5
|
+
export declare function resolveAwsProfile(profile?: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Whether the shared config file participates in profile/region resolution.
|
|
8
|
+
* Explicit profile selection enables it; the implicit default profile follows
|
|
9
|
+
* the AWS SDK's `AWS_SDK_LOAD_CONFIG` opt-in.
|
|
10
|
+
*/
|
|
11
|
+
export declare function shouldLoadAwsSharedConfig(profile?: string): boolean;
|
|
12
|
+
export declare function resolveAwsProfileRegion(profile?: string): string | undefined;
|
|
13
|
+
/** Region selected by the environment or active shared-config profile. */
|
|
14
|
+
export declare function resolveAwsAmbientRegion(profile?: string): string | undefined;
|
|
15
|
+
/** Resolve the region precedence shared by AWS transports and credential exchanges. */
|
|
16
|
+
export declare function resolveAwsRegion(explicitRegion?: string, profile?: string): string;
|
|
17
|
+
export declare function hasConfiguredAwsProfile(profile?: string): boolean;
|
|
@@ -8,6 +8,15 @@ import type { AssistantMessage, Model, ToolCall } from "../types.js";
|
|
|
8
8
|
* the persisted transcript keeps the byte-for-byte original.
|
|
9
9
|
*/
|
|
10
10
|
export declare function escapeHarmonyControlTokens(text: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Escape reserved Harmony control tokens inside a JSON document string (e.g.
|
|
13
|
+
* `function_call.arguments`). Doubles the backslash so the document remains
|
|
14
|
+
* valid JSON whose *decoded* strings carry the inert `<\|token\|>` spelling.
|
|
15
|
+
* `<|` cannot occur outside a string literal in valid JSON, so the blanket
|
|
16
|
+
* replace never corrupts structure; malformed documents are escaped
|
|
17
|
+
* best-effort.
|
|
18
|
+
*/
|
|
19
|
+
export declare function escapeHarmonyControlTokensInJson(text: string): string;
|
|
11
20
|
/**
|
|
12
21
|
* Whether requests to `model` are served by a Harmony-dialect backend
|
|
13
22
|
* (gpt-5.x / gpt-oss), which rejects reserved control-token spellings appearing
|
|
@@ -9,10 +9,16 @@
|
|
|
9
9
|
* literal enums/consts, and descriptions survive.
|
|
10
10
|
*/
|
|
11
11
|
export interface JsonSchemaToTsOptions {
|
|
12
|
-
/** Indentation unit for nested object bodies. Default two spaces. */
|
|
12
|
+
/** Indentation unit for nested object bodies. Default two spaces (none in `harmony` style). */
|
|
13
13
|
readonly indent?: string;
|
|
14
|
-
/** Emit `description` keywords as
|
|
14
|
+
/** Emit `description` keywords as comments on object properties. Default true. */
|
|
15
15
|
readonly comments?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Output flavor. `default` renders JSDoc comments, `;` delimiters, and
|
|
18
|
+
* indented bodies; `harmony` renders the flat OpenAI-Harmony convention —
|
|
19
|
+
* `//` line comments, `,` delimiters, no indentation.
|
|
20
|
+
*/
|
|
21
|
+
readonly style?: "default" | "harmony";
|
|
16
22
|
}
|
|
17
23
|
/** Convert a JSON Schema object into a simplified TypeScript type string. */
|
|
18
24
|
export declare function jsonSchemaToTypeScript(schema: unknown, options?: JsonSchemaToTsOptions): string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "17.2.
|
|
4
|
+
"version": "17.2.6",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.1",
|
|
41
|
-
"@oh-my-pi/pi-catalog": "17.2.
|
|
42
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
43
|
-
"@oh-my-pi/pi-wire": "17.2.
|
|
41
|
+
"@oh-my-pi/pi-catalog": "17.2.6",
|
|
42
|
+
"@oh-my-pi/pi-utils": "17.2.6",
|
|
43
|
+
"@oh-my-pi/pi-wire": "17.2.6",
|
|
44
44
|
"arktype": "2.2.3",
|
|
45
45
|
"zod": "^4"
|
|
46
46
|
},
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import {
|
|
9
|
+
$envExact,
|
|
9
10
|
getAgentDbPath,
|
|
10
11
|
getAgentDir,
|
|
11
12
|
getAuthBrokerSnapshotCachePath,
|
|
@@ -55,7 +56,7 @@ export function getAuthBrokerTokenFilePath(): string {
|
|
|
55
56
|
*/
|
|
56
57
|
async function defaultResolveConfigValue(config: string): Promise<string | undefined> {
|
|
57
58
|
if (config.startsWith("!")) return undefined;
|
|
58
|
-
const envValue =
|
|
59
|
+
const envValue = $envExact(config);
|
|
59
60
|
return envValue || config;
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* exception (standard type keeps extra keys): it preserves provider-specific extension fields so
|
|
13
13
|
* they round-trip through the broker instead of being dropped (see below).
|
|
14
14
|
*/
|
|
15
|
-
import { type Type
|
|
15
|
+
import { scope, type Type } from "arktype";
|
|
16
16
|
import {
|
|
17
17
|
type ApiKeyCredential,
|
|
18
18
|
type AuthCredential,
|
|
@@ -84,6 +84,11 @@ export interface AuthBrokerWireSchemas {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function buildAuthBrokerWireSchemas(): AuthBrokerWireSchemas {
|
|
87
|
+
// Wire schemas validate only a handful of times per process, so ArkType's
|
|
88
|
+
// definition-time JIT codegen is startup tax. A local jitless scope skips
|
|
89
|
+
// that codegen and uses interpreted traversal; correctness is unchanged.
|
|
90
|
+
const { type } = scope({}, { jitless: true });
|
|
91
|
+
|
|
87
92
|
// ─── Credential payloads ───────────────────────────────────────────────────
|
|
88
93
|
|
|
89
94
|
/** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
|
package/src/auth-storage.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { createHash } from "node:crypto";
|
|
|
12
12
|
import * as fs from "node:fs/promises";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import { parseAlibabaTokenPlanCredential } from "@oh-my-pi/pi-catalog/wire/alibaba-token-plan";
|
|
15
|
-
import { $env, getAgentDbPath, getDbBusyTimeoutMs, logger } from "@oh-my-pi/pi-utils";
|
|
15
|
+
import { $env, $envExact, getAgentDbPath, getDbBusyTimeoutMs, logger } from "@oh-my-pi/pi-utils";
|
|
16
16
|
import type { ApiKeyResolver } from "./auth-retry";
|
|
17
17
|
import * as AIError from "./error";
|
|
18
18
|
import { isUsageLimitOutcome } from "./error/rate-limit";
|
|
@@ -643,7 +643,7 @@ export type AuthStorageOptions = {
|
|
|
643
643
|
* Does NOT support "!command" syntax (that requires pi-natives).
|
|
644
644
|
*/
|
|
645
645
|
async function defaultConfigValueResolver(config: string): Promise<string | undefined> {
|
|
646
|
-
const envValue =
|
|
646
|
+
const envValue = $envExact(config);
|
|
647
647
|
return envValue || config;
|
|
648
648
|
}
|
|
649
649
|
|
package/src/dialect/examples.ts
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import type { Dialect, InbandTool } from "./types";
|
|
1
|
+
import { pyCall } from "./rendering";
|
|
2
|
+
import type { InbandTool } from "./types";
|
|
4
3
|
|
|
5
4
|
const INTENT_PLACEHOLDER = "…";
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Render a tool's examples as an `<examples>` block. Calls render in Python
|
|
8
|
+
* keyword-argument syntax (`name(key="value", n=1)`) regardless of the model's
|
|
9
|
+
* tool-call dialect, so example bytes stay identical across models. Multiline
|
|
10
|
+
* string args render as verbatim `"""…"""` blocks, and a call whose only
|
|
11
|
+
* argument is a string renders as the bare value — the block already names the
|
|
12
|
+
* tool, and payload args (commands, code, patches) read best verbatim.
|
|
13
|
+
*/
|
|
14
|
+
export function renderToolExamples(tool: InbandTool, intentField?: string): string {
|
|
8
15
|
const examples = tool.examples;
|
|
9
16
|
if (!examples?.length) return "";
|
|
10
|
-
const definition = getDialectDefinition(dialect);
|
|
11
17
|
const renderCall = (args: Record<string, unknown>): string => {
|
|
18
|
+
const bare = bareStringArg(args);
|
|
19
|
+
if (bare !== undefined) {
|
|
20
|
+
// Bare payload. The intent placeholder still rides on the envelope so
|
|
21
|
+
// intent-traced schemas (where `i` is required) keep teaching it.
|
|
22
|
+
const intentAttr = intentField ? ` ${intentField}="${INTENT_PLACEHOLDER}"` : "";
|
|
23
|
+
return `<example${intentAttr}>\n${bare}\n</example>`;
|
|
24
|
+
}
|
|
12
25
|
// When intent tracing injects `i` into the schema, examples must show a
|
|
13
26
|
// placeholder so the model learns to emit it. Keep it first, matching the
|
|
14
27
|
// schema injection order.
|
|
15
28
|
const finalArgs = intentField ? { [intentField]: INTENT_PLACEHOLDER, ...args } : args;
|
|
16
|
-
|
|
17
|
-
type: "toolCall",
|
|
18
|
-
id: "example",
|
|
19
|
-
name: tool.name,
|
|
20
|
-
arguments: finalArgs,
|
|
21
|
-
};
|
|
22
|
-
return `<example>\n${definition.renderToolCall(call, { tools: [tool], example: true }).trim()}\n</example>`;
|
|
29
|
+
return `<example>\n${pyCall(tool.name, finalArgs)}\n</example>`;
|
|
23
30
|
};
|
|
24
31
|
const parts = examples.map(ex => {
|
|
25
32
|
const head = ex.caption ? `# ${ex.caption}\n` : "";
|
|
@@ -31,3 +38,34 @@ export function renderToolExamples(tool: InbandTool, dialect: Dialect, intentFie
|
|
|
31
38
|
});
|
|
32
39
|
return `<examples>\n${parts.join("\n")}\n</examples>`;
|
|
33
40
|
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Render a tool's examples as JSDoc-style `@example` lines for comment-gutter
|
|
44
|
+
* contexts (the Harmony `namespace functions` inventory): `@example "caption"`
|
|
45
|
+
* followed by the call in the same Python kwargs syntax as the wire block. The
|
|
46
|
+
* tag line delimits each example, so no XML envelope is needed — which is why
|
|
47
|
+
* the inventory uses this instead of `//`-prefixing the `<examples>` block.
|
|
48
|
+
*/
|
|
49
|
+
export function renderToolExamplesJsdoc(tool: InbandTool): string {
|
|
50
|
+
const examples = tool.examples;
|
|
51
|
+
if (!examples?.length) return "";
|
|
52
|
+
const renderCall = (args: Record<string, unknown>): string => bareStringArg(args) ?? pyCall(tool.name, args);
|
|
53
|
+
const parts = examples.map(ex => {
|
|
54
|
+
const head = ex.caption ? `@example ${JSON.stringify(ex.caption)}` : "@example";
|
|
55
|
+
if ("call" in ex) return `${head}\n${renderCall(ex.call)}`;
|
|
56
|
+
if ("good" in ex) return `${head}\nWRONG:\n${renderCall(ex.bad)}\nRIGHT:\n${renderCall(ex.good)}`;
|
|
57
|
+
return ex.note ? `${head}\n${ex.note}` : head;
|
|
58
|
+
});
|
|
59
|
+
return parts.join("\n");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Sole-argument string payload, if the call has exactly one string argument. */
|
|
63
|
+
function bareStringArg(args: Record<string, unknown>): string | undefined {
|
|
64
|
+
let sole: unknown;
|
|
65
|
+
let count = 0;
|
|
66
|
+
for (const key in args) {
|
|
67
|
+
count++;
|
|
68
|
+
sole = args[key];
|
|
69
|
+
}
|
|
70
|
+
return count === 1 && typeof sole === "string" ? sole : undefined;
|
|
71
|
+
}
|