@salesforce/sfdx-agent-harness-mastra 0.99.0 → 0.101.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 +10 -0
- package/README.md +5 -11
- package/dist/index.d.ts +5 -5
- package/dist/mastra-harness-factory.d.ts +3 -3
- package/dist/mastra-harness.d.ts +8 -9
- package/dist/mastra-harness.js +9 -10
- package/dist/mastra-tool-mapper.js +2 -3
- package/dist/pass-through-anthropic.d.ts +17 -17
- package/dist/pass-through-anthropic.js +21 -23
- package/dist/pass-through-openai-chat-completions.d.ts +12 -12
- package/dist/pass-through-openai-chat-completions.js +15 -17
- package/dist/pass-through-openai.d.ts +9 -9
- package/dist/pass-through-openai.js +14 -16
- package/dist/retry-pre-first-token.d.ts +2 -2
- package/dist/session-context-store.d.ts +1 -1
- package/dist/session-context-store.js +1 -1
- package/dist/stream-errors.d.ts +1 -1
- package/dist/stream-errors.js +1 -1
- package/dist/{v3-stream-mapping.d.ts → stream-mapping.d.ts} +62 -47
- package/dist/{v3-stream-mapping.js → stream-mapping.js} +51 -32
- package/dist/tool-args-marker.d.ts +2 -2
- package/dist/tool-args-marker.js +2 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to `@salesforce/sfdx-agent-harness-mastra` are documented in this file.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [0.101.0] - 2026-10-13
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
- **harness-mastra**: migrate pass-throughs to LanguageModelV4 and remove chat-generations @W-24224624@ ([#876](https://github.com/forcedotcom/agentic-dx/pull/876))
|
|
10
|
+
|
|
11
|
+
## [0.100.0] - 2026-10-09
|
|
12
|
+
|
|
13
|
+
### Chores
|
|
14
|
+
- **agent-sdk,harness-claude,harness-mastra,harness-openai**: remove the openai harness @W-24458323@ ([#871](https://github.com/forcedotcom/agentic-dx/pull/871))
|
|
15
|
+
|
|
6
16
|
## [0.99.0] - 2026-10-06
|
|
7
17
|
|
|
8
18
|
### Features
|
package/README.md
CHANGED
|
@@ -59,8 +59,8 @@ Linux and macOS release the handle eagerly; the retry options are no-ops on thos
|
|
|
59
59
|
| ----------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
60
|
| `MastraHarnessFactory` | class | `HarnessFactory` that produces a Mastra-backed `AgentHarness` bound to a storage root. |
|
|
61
61
|
| `MastraHarnessFactoryConfig` | interface | Configuration for `MastraHarnessFactory` (`toolApprovalTimeoutMs?`, `languageModelBuilders?`). |
|
|
62
|
-
| `MastraLanguageModelBuilderContext` | type | Context bag the harness threads into each `
|
|
63
|
-
| `MastraLanguageModelBuilder` | type | Factory function shape for a consumer-injected builder: `(ctx: MastraLanguageModelBuilderContext) =>
|
|
62
|
+
| `MastraLanguageModelBuilderContext` | type | Context bag the harness threads into each `LanguageModelV4` builder (`getInfo`, `logBus`, `emitWireCommunication`, `innerFetch`). |
|
|
63
|
+
| `MastraLanguageModelBuilder` | type | Factory function shape for a consumer-injected builder: `(ctx: MastraLanguageModelBuilderContext) => LanguageModelV4`. |
|
|
64
64
|
| `MASTRA_BUILT_IN_TOOL_POLICIES` | const | Frozen `ReadonlyArray<ToolPolicyRule>` the harness feeds the SDK's `resolveToolApprovalPolicy` as the `tiers.harness` slice — `allow` rules for Mastra's built-in `updateWorkingMemory` / `skill` / `skill_search` / `skill_read` tools. |
|
|
65
65
|
| `MASTRA_BUILT_IN_TOOL_NAMES` | const | The four built-in tool leaf names `MASTRA_BUILT_IN_TOOL_POLICIES` covers. |
|
|
66
66
|
|
|
@@ -81,20 +81,14 @@ no `globalThis` mutation. When no proxy env is set, `fetch` runs unwrapped (zero
|
|
|
81
81
|
|
|
82
82
|
#### Injecting custom wire shapes
|
|
83
83
|
|
|
84
|
-
A consumer can register a `
|
|
84
|
+
A consumer can register a `LanguageModelV4` builder for a custom `providerHint` via
|
|
85
85
|
`MastraHarnessFactoryConfig.languageModelBuilders` without editing the harness's internal switch. Each injected key
|
|
86
|
-
becomes a supported provider hint and a dispatch arm. Used to inject wire shapes the harness does not ship natively
|
|
87
|
-
(e.g., a temporary GovCloud `chat/generations` fallback).
|
|
86
|
+
becomes a supported provider hint and a dispatch arm. Used to inject wire shapes the harness does not ship natively.
|
|
88
87
|
|
|
89
88
|
```ts
|
|
90
|
-
import {
|
|
91
|
-
CHAT_GENERATIONS_PROVIDER_HINT,
|
|
92
|
-
buildChatGenerationsLanguageModel,
|
|
93
|
-
} from '@salesforce/sfdx-agent-chat-generations';
|
|
94
|
-
|
|
95
89
|
const factory = new MastraHarnessFactory({
|
|
96
90
|
languageModelBuilders: {
|
|
97
|
-
|
|
91
|
+
'my-custom-wire-shape': (ctx) => buildMyLanguageModel(ctx),
|
|
98
92
|
},
|
|
99
93
|
});
|
|
100
94
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { LogBus } from '@salesforce/agentic-common';
|
|
2
2
|
import type { ModelConnectivityInfo, WireCommunicationEvent } from '@salesforce/sfdx-agent-sdk';
|
|
3
|
-
import type {
|
|
3
|
+
import type { LanguageModelV4 } from '@ai-sdk/provider';
|
|
4
4
|
export { MastraHarnessFactory, type MastraHarnessFactoryConfig } from './mastra-harness-factory.js';
|
|
5
5
|
export type { MastraAgentHarness } from './mastra-harness.js';
|
|
6
6
|
export { MASTRA_BUILT_IN_TOOL_NAMES, MASTRA_BUILT_IN_TOOL_POLICIES } from './mastra-built-in-policies.js';
|
|
7
7
|
export { type MastraAgentConfig, type MastraSkillSearchConfig, type MastraToolSearchConfig, type SkillSearchHandle, type ToolSearchHandle, } from './tool-search.js';
|
|
8
8
|
/**
|
|
9
|
-
* Context the harness threads into each `
|
|
9
|
+
* Context the harness threads into each `LanguageModelV4` builder — both the
|
|
10
10
|
* built-in builders and any consumer-injected builders registered via
|
|
11
11
|
* `MastraHarnessFactoryConfig.languageModelBuilders`. The shape mirrors what
|
|
12
12
|
* `MastraHarness.buildLanguageModel` currently hands its internal builders, so an
|
|
@@ -33,10 +33,10 @@ export type MastraLanguageModelBuilderContext = {
|
|
|
33
33
|
innerFetch: typeof fetch;
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
|
-
* Factory function shape for a consumer-injected `
|
|
36
|
+
* Factory function shape for a consumer-injected `LanguageModelV4` builder.
|
|
37
37
|
* Registered via `MastraHarnessFactoryConfig.languageModelBuilders` keyed by a custom
|
|
38
38
|
* `providerHint`, used to inject wire shapes the harness does not ship natively
|
|
39
|
-
* (e.g. a
|
|
39
|
+
* (e.g. a bespoke gateway wire format). The builder receives the
|
|
40
40
|
* same context bag the harness's built-in pass-through builders get, so an injected
|
|
41
41
|
* builder can emit `WireCommunicationEvent`s and honor `HTTPS_PROXY` routing without
|
|
42
42
|
* duplicating the harness's own fetch wrapper / wire-monitoring plumbing. The
|
|
@@ -55,4 +55,4 @@ export type MastraLanguageModelBuilderContext = {
|
|
|
55
55
|
* });
|
|
56
56
|
* ```
|
|
57
57
|
*/
|
|
58
|
-
export type MastraLanguageModelBuilder = (ctx: MastraLanguageModelBuilderContext) =>
|
|
58
|
+
export type MastraLanguageModelBuilder = (ctx: MastraLanguageModelBuilderContext) => LanguageModelV4;
|
|
@@ -12,10 +12,10 @@ export interface MastraHarnessFactoryConfig {
|
|
|
12
12
|
*/
|
|
13
13
|
toolApprovalTimeoutMs?: number;
|
|
14
14
|
/**
|
|
15
|
-
* Consumer-registered `
|
|
15
|
+
* Consumer-registered `LanguageModelV4` builders keyed by `providerHint`. Each key
|
|
16
16
|
* becomes a supported provider hint and a dispatch arm in `buildLanguageModel`. Used
|
|
17
|
-
* to inject wire shapes the harness does not ship natively (e.g. a
|
|
18
|
-
*
|
|
17
|
+
* to inject wire shapes the harness does not ship natively (e.g. a bespoke
|
|
18
|
+
* gateway wire format). Keys MUST NOT collide with the
|
|
19
19
|
* harness's built-in hints (`anthropic`, `bedrock-anthropic`, `openai`,
|
|
20
20
|
* `openai-responses`, `openai-compatible`) — constructing with a colliding key
|
|
21
21
|
* throws at factory construction time rather than silently shadowing the built-in.
|
package/dist/mastra-harness.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { MastraApprovalCoordinator } from './mastra-approval-coordinator.js';
|
|
|
8
8
|
import { type HttpErrorCapture } from './mcp-config-mapper.js';
|
|
9
9
|
import { type MastraAgentConfig, type SkillSearchHandle, type ToolSearchHandle } from './tool-search.js';
|
|
10
10
|
import { type MultimodalFile } from '@salesforce/sfdx-agent-sdk';
|
|
11
|
-
import type {
|
|
11
|
+
import type { LanguageModelV4 } from '@ai-sdk/provider';
|
|
12
12
|
import { Clock, type LogRecord, type BackoffRetryerDeps, type UniqueIDGenerator, type Unsubscribe } from '@salesforce/agentic-common';
|
|
13
13
|
import { type ToolsInput } from './utils.js';
|
|
14
14
|
import { Workspace } from '@mastra/core/workspace';
|
|
@@ -247,13 +247,13 @@ export interface MastraHarnessOptions {
|
|
|
247
247
|
*/
|
|
248
248
|
innerFetch?: typeof fetch;
|
|
249
249
|
/**
|
|
250
|
-
* Consumer-registered `
|
|
250
|
+
* Consumer-registered `LanguageModelV4` builders keyed by `providerHint`.
|
|
251
251
|
* Forwarded from `MastraHarnessFactoryConfig.languageModelBuilders`. The
|
|
252
252
|
* harness consults this map in `buildLanguageModel`'s `default` arm before
|
|
253
253
|
* throwing `MODEL_NOT_SUPPORTED_BY_HARNESS`, so an injected builder can
|
|
254
254
|
* handle a custom hint without editing the harness's internal switch.
|
|
255
255
|
*/
|
|
256
|
-
languageModelBuilders?: Readonly<Record<string, (ctx: MastraLanguageModelBuilderContext) =>
|
|
256
|
+
languageModelBuilders?: Readonly<Record<string, (ctx: MastraLanguageModelBuilderContext) => LanguageModelV4>>;
|
|
257
257
|
/**
|
|
258
258
|
* Clock / sleep / RNG for the pre-first-token LLM retry loop (W-24236393), whose policy is
|
|
259
259
|
* `PRE_FIRST_TOKEN_RETRY_OPTIONS`. Tests inject a no-sleep `sleep`.
|
|
@@ -327,9 +327,8 @@ export declare class MastraHarness implements MastraAgentHarness {
|
|
|
327
327
|
* counter owned by the language model would bleed across turns AND across
|
|
328
328
|
* concurrent threads; owning it here — the innermost map reset at each
|
|
329
329
|
* `stream()` (per-turn) and keyed by thread (per-thread) — matches Claude's
|
|
330
|
-
* per-`stream()` `toolArgFailureCounts
|
|
331
|
-
*
|
|
332
|
-
* across all three harnesses. Nested (rather than a `${agentId}::${threadId}`
|
|
330
|
+
* per-`stream()` `toolArgFailureCounts`, so the exhaustion-clause wording is
|
|
331
|
+
* identical across both harnesses. Nested (rather than a `${agentId}::${threadId}`
|
|
333
332
|
* string key) so a consumer id containing the delimiter can't alias two
|
|
334
333
|
* distinct scopes, and cleanup is O(1): `destroyThread` deletes the thread
|
|
335
334
|
* entry, `destroyAgent` deletes the agent entry, `shutdown` clears all.
|
|
@@ -515,7 +514,7 @@ export declare class MastraHarness implements MastraAgentHarness {
|
|
|
515
514
|
* non-empty paths.
|
|
516
515
|
*/
|
|
517
516
|
/**
|
|
518
|
-
* Dispatches on the bag's `providerHint` and returns a `
|
|
517
|
+
* Dispatches on the bag's `providerHint` and returns a `LanguageModelV4` that
|
|
519
518
|
* speaks the provider-native pass-through endpoint. The builder closes over
|
|
520
519
|
* `getInfo` so within-shape `modelId` swaps, JWT rotations, and
|
|
521
520
|
* premium-rate-limit gate flips land on the next outbound call without
|
|
@@ -589,8 +588,8 @@ export declare class MastraHarness implements MastraAgentHarness {
|
|
|
589
588
|
* `agent.stream()` accepts.
|
|
590
589
|
*
|
|
591
590
|
* `text` parts collapse into text content blocks; `image` / `file` parts become v5 `file`
|
|
592
|
-
* content blocks (`mediaType` + base64 `data` + optional `filename`) that Mastra
|
|
593
|
-
*
|
|
591
|
+
* content blocks (`mediaType` + base64 `data` + optional `filename`) that Mastra normalizes to V4
|
|
592
|
+
* `file` parts for the provider pass-through translators. Pre-stream validation and the
|
|
594
593
|
* input-part guard live in the SDK's {@link lowerStreamInput}, so file caps and the
|
|
595
594
|
* `MULTIMODAL_NOT_SUPPORTED` / `INVALID_MESSAGE_CONTENT` semantics are identical to the Claude
|
|
596
595
|
* harness; this method only supplies the Mastra-specific lowering of each part.
|
package/dist/mastra-harness.js
CHANGED
|
@@ -27,7 +27,7 @@ import { validateMultimodalFiles } from '@salesforce/sfdx-agent-sdk';
|
|
|
27
27
|
import { buildAnthropicDirectLanguageModel, buildAnthropicLanguageModel } from './pass-through-anthropic.js';
|
|
28
28
|
import { buildOpenAIChatCompletionsLanguageModel } from './pass-through-openai-chat-completions.js';
|
|
29
29
|
import { buildOpenAILanguageModel } from './pass-through-openai.js';
|
|
30
|
-
import { readToolArgsRetryScope, TOOL_ARGS_RETRY_SCOPE_AGENT_ID, TOOL_ARGS_RETRY_SCOPE_PROVIDER, TOOL_ARGS_RETRY_SCOPE_THREAD_ID, } from './
|
|
30
|
+
import { readToolArgsRetryScope, TOOL_ARGS_RETRY_SCOPE_AGENT_ID, TOOL_ARGS_RETRY_SCOPE_PROVIDER, TOOL_ARGS_RETRY_SCOPE_THREAD_ID, } from './stream-mapping.js';
|
|
31
31
|
import { ToolResultRedactionProcessor } from './mastra-tool-result-redaction-processor.js';
|
|
32
32
|
import { backfillCreatedAt, buildSummaryPrompt, Clock, getErrorMessage, RealClock, splitFrontmatterAndBody, UUIDGenerator, } from '@salesforce/agentic-common';
|
|
33
33
|
import { withPreFirstTokenRetry } from './retry-pre-first-token.js';
|
|
@@ -495,9 +495,8 @@ export class MastraHarness {
|
|
|
495
495
|
* counter owned by the language model would bleed across turns AND across
|
|
496
496
|
* concurrent threads; owning it here — the innermost map reset at each
|
|
497
497
|
* `stream()` (per-turn) and keyed by thread (per-thread) — matches Claude's
|
|
498
|
-
* per-`stream()` `toolArgFailureCounts
|
|
499
|
-
*
|
|
500
|
-
* across all three harnesses. Nested (rather than a `${agentId}::${threadId}`
|
|
498
|
+
* per-`stream()` `toolArgFailureCounts`, so the exhaustion-clause wording is
|
|
499
|
+
* identical across both harnesses. Nested (rather than a `${agentId}::${threadId}`
|
|
501
500
|
* string key) so a consumer id containing the delimiter can't alias two
|
|
502
501
|
* distinct scopes, and cleanup is O(1): `destroyThread` deletes the thread
|
|
503
502
|
* entry, `destroyAgent` deletes the agent entry, `shutdown` clears all.
|
|
@@ -1551,7 +1550,7 @@ export class MastraHarness {
|
|
|
1551
1550
|
* non-empty paths.
|
|
1552
1551
|
*/
|
|
1553
1552
|
/**
|
|
1554
|
-
* Dispatches on the bag's `providerHint` and returns a `
|
|
1553
|
+
* Dispatches on the bag's `providerHint` and returns a `LanguageModelV4` that
|
|
1555
1554
|
* speaks the provider-native pass-through endpoint. The builder closes over
|
|
1556
1555
|
* `getInfo` so within-shape `modelId` swaps, JWT rotations, and
|
|
1557
1556
|
* premium-rate-limit gate flips land on the next outbound call without
|
|
@@ -1672,9 +1671,9 @@ export class MastraHarness {
|
|
|
1672
1671
|
}));
|
|
1673
1672
|
default: {
|
|
1674
1673
|
// Consult the injected builders map before throwing. This is the
|
|
1675
|
-
// extension point for consumer-registered wire shapes (e.g. a
|
|
1676
|
-
//
|
|
1677
|
-
//
|
|
1674
|
+
// extension point for consumer-registered wire shapes (e.g. a bespoke
|
|
1675
|
+
// gateway wire format). The builder receives the same context bag the
|
|
1676
|
+
// built-in builders get.
|
|
1678
1677
|
const custom = this.languageModelBuilders?.[initial.providerHint];
|
|
1679
1678
|
if (custom) {
|
|
1680
1679
|
return this.wrapLanguageModel(custom({ getInfo, logBus, emitWireCommunication, innerFetch }));
|
|
@@ -1774,8 +1773,8 @@ export class MastraHarness {
|
|
|
1774
1773
|
* `agent.stream()` accepts.
|
|
1775
1774
|
*
|
|
1776
1775
|
* `text` parts collapse into text content blocks; `image` / `file` parts become v5 `file`
|
|
1777
|
-
* content blocks (`mediaType` + base64 `data` + optional `filename`) that Mastra
|
|
1778
|
-
*
|
|
1776
|
+
* content blocks (`mediaType` + base64 `data` + optional `filename`) that Mastra normalizes to V4
|
|
1777
|
+
* `file` parts for the provider pass-through translators. Pre-stream validation and the
|
|
1779
1778
|
* input-part guard live in the SDK's {@link lowerStreamInput}, so file caps and the
|
|
1780
1779
|
* `MULTIMODAL_NOT_SUPPORTED` / `INVALID_MESSAGE_CONTENT` semantics are identical to the Claude
|
|
1781
1780
|
* harness; this method only supplies the Mastra-specific lowering of each part.
|
|
@@ -54,9 +54,8 @@ export function mapToolDefinitionsToMastraTools(tools, parkerLookup) {
|
|
|
54
54
|
inputSchema: toStandardSchema(ensureJsonSchemaMarker(tool.inputSchema)),
|
|
55
55
|
// `strict: false` keeps a provider compat-layer (OpenAI/Anthropic
|
|
56
56
|
// strict-mode) from RE-degrading the preserved schema on the way to the
|
|
57
|
-
// wire — arbitrary consumer schemas aren't structured-output schemas
|
|
58
|
-
//
|
|
59
|
-
// same reason). Without it, `CoreToolBuilder.build` applies a matching
|
|
57
|
+
// wire — arbitrary consumer schemas aren't structured-output schemas.
|
|
58
|
+
// Without it, `CoreToolBuilder.build` applies a matching
|
|
60
59
|
// compat layer that reintroduces `additionalProperties:false`.
|
|
61
60
|
strict: false,
|
|
62
61
|
// Issue #529 step 2c (Path A) — consumer tools bypass the
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4Prompt } from '@ai-sdk/provider';
|
|
3
3
|
import type { LogBus } from '@salesforce/agentic-common';
|
|
4
4
|
import type { ModelConnectivityInfo, WireCommunicationEvent } from '@salesforce/sfdx-agent-sdk';
|
|
5
|
-
import { type GatewayFetchWireHooks } from './
|
|
5
|
+
import { type GatewayFetchWireHooks } from './stream-mapping.js';
|
|
6
6
|
/**
|
|
7
|
-
* Builds the in-process `
|
|
7
|
+
* Builds the in-process `LanguageModelV4` impl for the Salesforce LLM Gateway's
|
|
8
8
|
* `/invoke-with-response-stream` (Bedrock-Anthropic pass-through) endpoint.
|
|
9
9
|
* Drives `@anthropic-ai/bedrock-sdk` with `skipAuth: true` so the connectivity
|
|
10
10
|
* resolver's headers (Salesforce JWT, tenant key, etc.) are the single source
|
|
@@ -50,9 +50,9 @@ export declare function buildAnthropicLanguageModel(opts: {
|
|
|
50
50
|
* `standardSchemaToJSONSchema`), which injects `additionalProperties: false`
|
|
51
51
|
* onto any object node that declared none (JSON Schema's default is open).
|
|
52
52
|
* Gating against that closed schema would reject extra keys on an
|
|
53
|
-
* implicitly-open nested object and diverge from Claude
|
|
53
|
+
* implicitly-open nested object and diverge from Claude (which gates
|
|
54
54
|
* the pristine declared schema); validating the declared schema keeps the
|
|
55
|
-
* gate verdict identical across
|
|
55
|
+
* gate verdict identical across both harnesses (W-24023028). Same
|
|
56
56
|
* per-config-snapshot freshness caveat as `getConsumerToolNames`. Defaults to
|
|
57
57
|
* an empty map (no schema → the gate treats every consumer tool as
|
|
58
58
|
* unvalidatable-`ok`) for callers that don't thread it.
|
|
@@ -66,12 +66,12 @@ export declare function buildAnthropicLanguageModel(opts: {
|
|
|
66
66
|
* `LanguageModel`. Defaults to a single per-instance map for callers that
|
|
67
67
|
* don't thread it (tests / consumer-injected builders).
|
|
68
68
|
*/
|
|
69
|
-
getToolArgsRetryCounts?: (options:
|
|
69
|
+
getToolArgsRetryCounts?: (options: LanguageModelV4CallOptions) => Map<string, number>;
|
|
70
70
|
/** Per-read idle timeout (ms) for the gateway response body; see `createGatewayFetch`. */
|
|
71
71
|
idleReadTimeoutMs?: number;
|
|
72
|
-
}):
|
|
72
|
+
}): LanguageModelV4;
|
|
73
73
|
/**
|
|
74
|
-
* Builds the in-process `
|
|
74
|
+
* Builds the in-process `LanguageModelV4` impl for direct-Anthropic API access
|
|
75
75
|
* (`api.anthropic.com/v1/messages` or any Anthropic-shaped endpoint). Drives
|
|
76
76
|
* `@anthropic-ai/sdk` with the api-key peeled from the resolver's headers.
|
|
77
77
|
*
|
|
@@ -101,10 +101,10 @@ export declare function buildAnthropicDirectLanguageModel(opts: {
|
|
|
101
101
|
/** See {@link buildAnthropicLanguageModel} `getConsumerToolSchemas`. */
|
|
102
102
|
getConsumerToolSchemas?: () => ReadonlyMap<string, Record<string, unknown>>;
|
|
103
103
|
/** See {@link buildAnthropicLanguageModel} `getToolArgsRetryCounts`. */
|
|
104
|
-
getToolArgsRetryCounts?: (options:
|
|
104
|
+
getToolArgsRetryCounts?: (options: LanguageModelV4CallOptions) => Map<string, number>;
|
|
105
105
|
/** Per-read idle timeout (ms) for the gateway response body; see `createGatewayFetch`. */
|
|
106
106
|
idleReadTimeoutMs?: number;
|
|
107
|
-
}):
|
|
107
|
+
}): LanguageModelV4;
|
|
108
108
|
/**
|
|
109
109
|
* Stamps `cache_control: {type:'ephemeral'}` on up to three of Anthropic's four
|
|
110
110
|
* allowed breakpoints: the last tool (caches the constant tools block), the
|
|
@@ -115,25 +115,25 @@ export declare function buildAnthropicDirectLanguageModel(opts: {
|
|
|
115
115
|
* only the turn-constant tools/system cached and forfeit most of the win.
|
|
116
116
|
* Mutates only `commonParams`'s own arrays — which
|
|
117
117
|
* `translate*ToAnthropic` builds fresh per request — never the caller's
|
|
118
|
-
* original `
|
|
118
|
+
* original `LanguageModelV4` prompt/tool objects.
|
|
119
119
|
*/
|
|
120
120
|
export declare function applyCacheBreakpoints(commonParams: Omit<Anthropic.Messages.MessageCreateParamsStreaming, 'stream'>): void;
|
|
121
121
|
/**
|
|
122
|
-
* Translates a
|
|
122
|
+
* Translates a V4 `LanguageModelV4Prompt` into Anthropic's Messages API shape
|
|
123
123
|
* — top-level `system` string + `MessageParam[]` of role-tagged content blocks.
|
|
124
124
|
*
|
|
125
|
-
*
|
|
125
|
+
* V4 keeps system / user / assistant / tool messages flat. Anthropic combines
|
|
126
126
|
* tool results onto a synthetic user-role turn carrying `tool_result` blocks,
|
|
127
|
-
* so we collapse consecutive tool-only
|
|
127
|
+
* so we collapse consecutive tool-only V4 messages into the surrounding user
|
|
128
128
|
* turn shape.
|
|
129
129
|
*/
|
|
130
|
-
export declare function translatePromptToAnthropic(prompt:
|
|
130
|
+
export declare function translatePromptToAnthropic(prompt: LanguageModelV4Prompt): {
|
|
131
131
|
system?: string;
|
|
132
132
|
messages: Anthropic.Messages.MessageParam[];
|
|
133
133
|
};
|
|
134
134
|
/**
|
|
135
|
-
* Translates
|
|
135
|
+
* Translates V4 function-tool definitions into Anthropic's `Tool[]` shape.
|
|
136
136
|
* Skips non-function variants (provider-defined / dynamic) — the pass-through
|
|
137
137
|
* path supports only the function-tool subset that Mastra's tool mapper produces.
|
|
138
138
|
*/
|
|
139
|
-
export declare function translateToolsToAnthropic(options:
|
|
139
|
+
export declare function translateToolsToAnthropic(options: LanguageModelV4CallOptions): Anthropic.Messages.Tool[];
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import { AnthropicBedrock } from '@anthropic-ai/bedrock-sdk';
|
|
6
6
|
import Anthropic from '@anthropic-ai/sdk';
|
|
7
7
|
import { EmptyStreamError, NetworkStreamError, isStreamTransportError, recognizeBedrockValidationError, } from './stream-errors.js';
|
|
8
|
-
import { SpanLifecycle, ToolInputSpanLifecycle, buildValidatedToolCallChunk, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapAnthropicFinishReason, mapAnthropicUsage, toolResultOutputToString, } from './
|
|
8
|
+
import { SpanLifecycle, ToolInputSpanLifecycle, buildValidatedToolCallChunk, createGatewayFetch, isEmptyUsage, isParameterExcluded, mapAnthropicFinishReason, mapAnthropicUsage, toolResultOutputToString, v4FileDataToBase64, } from './stream-mapping.js';
|
|
9
9
|
/**
|
|
10
|
-
* Builds the in-process `
|
|
10
|
+
* Builds the in-process `LanguageModelV4` impl for the Salesforce LLM Gateway's
|
|
11
11
|
* `/invoke-with-response-stream` (Bedrock-Anthropic pass-through) endpoint.
|
|
12
12
|
* Drives `@anthropic-ai/bedrock-sdk` with `skipAuth: true` so the connectivity
|
|
13
13
|
* resolver's headers (Salesforce JWT, tenant key, etc.) are the single source
|
|
@@ -44,7 +44,7 @@ export function buildAnthropicLanguageModel(opts) {
|
|
|
44
44
|
}),
|
|
45
45
|
};
|
|
46
46
|
return {
|
|
47
|
-
specificationVersion: '
|
|
47
|
+
specificationVersion: 'v4',
|
|
48
48
|
provider: 'salesforce-llm-gateway-pass-through-anthropic',
|
|
49
49
|
// Mastra captures `modelId` once at construction. The actual wire id
|
|
50
50
|
// sent on every call is read from the live bag via `commonParams.model`
|
|
@@ -57,7 +57,7 @@ export function buildAnthropicLanguageModel(opts) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* Builds the in-process `
|
|
60
|
+
* Builds the in-process `LanguageModelV4` impl for direct-Anthropic API access
|
|
61
61
|
* (`api.anthropic.com/v1/messages` or any Anthropic-shaped endpoint). Drives
|
|
62
62
|
* `@anthropic-ai/sdk` with the api-key peeled from the resolver's headers.
|
|
63
63
|
*
|
|
@@ -104,7 +104,7 @@ export function buildAnthropicDirectLanguageModel(opts) {
|
|
|
104
104
|
}),
|
|
105
105
|
};
|
|
106
106
|
return {
|
|
107
|
-
specificationVersion: '
|
|
107
|
+
specificationVersion: 'v4',
|
|
108
108
|
provider: 'anthropic-direct-pass-through',
|
|
109
109
|
modelId: initialInfo.nativeModelId,
|
|
110
110
|
supportedUrls: {},
|
|
@@ -231,7 +231,7 @@ const CACHE_CONTROL_EPHEMERAL = Object.freeze({ type: 'ephemeral' });
|
|
|
231
231
|
* only the turn-constant tools/system cached and forfeit most of the win.
|
|
232
232
|
* Mutates only `commonParams`'s own arrays — which
|
|
233
233
|
* `translate*ToAnthropic` builds fresh per request — never the caller's
|
|
234
|
-
* original `
|
|
234
|
+
* original `LanguageModelV4` prompt/tool objects.
|
|
235
235
|
*/
|
|
236
236
|
export function applyCacheBreakpoints(commonParams) {
|
|
237
237
|
const tools = commonParams.tools;
|
|
@@ -348,7 +348,7 @@ async function doGenerate(ctx, options) {
|
|
|
348
348
|
content.push(...buildValidatedToolCallChunk(block.id, block.name, argsBuffer, toolSchemas.get(block.name), ctx.getConsumerToolNames().has(block.name), { retryCounts: ctx.getToolArgsRetryCounts(options), logBus: ctx.logBus }));
|
|
349
349
|
}
|
|
350
350
|
// Other block types (redacted_thinking, server-tool-result, etc.) are
|
|
351
|
-
// not in the
|
|
351
|
+
// not in the V4 ChatEvent surface — same posture as doStream.
|
|
352
352
|
}
|
|
353
353
|
const finishReason = mapAnthropicFinishReason(message.stop_reason, sawToolCalls);
|
|
354
354
|
const usage = mapAnthropicUsage(message.usage);
|
|
@@ -398,7 +398,7 @@ async function doStream(ctx, options) {
|
|
|
398
398
|
const stream = new ReadableStream({
|
|
399
399
|
start: async (controller) => {
|
|
400
400
|
const textSpan = new SpanLifecycle();
|
|
401
|
-
// Extended-thinking narration streams on its own
|
|
401
|
+
// Extended-thinking narration streams on its own V4 reasoning span,
|
|
402
402
|
// so it surfaces as `reasoning-delta` (→ AG-UI REASONING_MESSAGE_CONTENT)
|
|
403
403
|
// rather than bleeding into the answer's `text-delta`.
|
|
404
404
|
const reasoningSpan = new SpanLifecycle('reasoning');
|
|
@@ -432,7 +432,7 @@ async function doStream(ctx, options) {
|
|
|
432
432
|
case 'content_block_start': {
|
|
433
433
|
const block = event.content_block;
|
|
434
434
|
if (block.type === 'text') {
|
|
435
|
-
// Open the
|
|
435
|
+
// Open the V4 text span on the first text block of the message
|
|
436
436
|
// (or after a tool_use rotated us forward).
|
|
437
437
|
textSpan.openIfNeeded(controller);
|
|
438
438
|
}
|
|
@@ -460,7 +460,7 @@ async function doStream(ctx, options) {
|
|
|
460
460
|
});
|
|
461
461
|
}
|
|
462
462
|
// Other block types (redacted_thinking, server-tool result, etc.)
|
|
463
|
-
// are intentionally ignored — they're not in the
|
|
463
|
+
// are intentionally ignored — they're not in the V4 ChatEvent surface.
|
|
464
464
|
break;
|
|
465
465
|
}
|
|
466
466
|
case 'content_block_delta': {
|
|
@@ -481,7 +481,7 @@ async function doStream(ctx, options) {
|
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
// signature_delta / citations_delta are not surfaced — the
|
|
484
|
-
// encrypted thinking signature has no
|
|
484
|
+
// encrypted thinking signature has no V4 stream-part and no
|
|
485
485
|
// consumer surface (AG-UI ReasoningEncryptedValue is deferred).
|
|
486
486
|
break;
|
|
487
487
|
}
|
|
@@ -526,7 +526,7 @@ async function doStream(ctx, options) {
|
|
|
526
526
|
}
|
|
527
527
|
// Stream ended without an explicit `message_stop`. If we saw at least
|
|
528
528
|
// one recognized content / usage event, close out the span and
|
|
529
|
-
// synthesize a terminal `finish` so
|
|
529
|
+
// synthesize a terminal `finish` so V4 consumers see exactly one. If
|
|
530
530
|
// we saw NONE — a clean-but-empty body — surface a classified `error`
|
|
531
531
|
// instead of a false-success empty `finish` (8a). A cancelled turn is
|
|
532
532
|
// exempt: an aborted stream ends cleanly here too (the SDK `Stream`
|
|
@@ -556,7 +556,7 @@ async function doStream(ctx, options) {
|
|
|
556
556
|
// Trailing-JSON #396: the gateway sometimes appends a JSON error envelope
|
|
557
557
|
// after the terminal eventstream frame on otherwise-successful responses,
|
|
558
558
|
// which the Anthropic SDK's parser surfaces as a late throw. Drop it
|
|
559
|
-
// silently —
|
|
559
|
+
// silently — V4 consumers must see exactly one terminal `finish`.
|
|
560
560
|
controller.close();
|
|
561
561
|
return;
|
|
562
562
|
}
|
|
@@ -574,7 +574,7 @@ async function doStream(ctx, options) {
|
|
|
574
574
|
});
|
|
575
575
|
return { stream };
|
|
576
576
|
}
|
|
577
|
-
/** Default Anthropic `max_tokens` when
|
|
577
|
+
/** Default Anthropic `max_tokens` when V4 doesn't supply one. The Bedrock invoke
|
|
578
578
|
* API requires the field; Mastra leaves it unset for many turns. 4096 mirrors
|
|
579
579
|
* the `@ai-sdk/anthropic` provider's default. */
|
|
580
580
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
@@ -590,12 +590,12 @@ const DEFAULT_MAX_TOKENS = 4096;
|
|
|
590
590
|
*/
|
|
591
591
|
const SUMMARY_MAX_OUTPUT_TOKENS = 16384;
|
|
592
592
|
/**
|
|
593
|
-
* Translates a
|
|
593
|
+
* Translates a V4 `LanguageModelV4Prompt` into Anthropic's Messages API shape
|
|
594
594
|
* — top-level `system` string + `MessageParam[]` of role-tagged content blocks.
|
|
595
595
|
*
|
|
596
|
-
*
|
|
596
|
+
* V4 keeps system / user / assistant / tool messages flat. Anthropic combines
|
|
597
597
|
* tool results onto a synthetic user-role turn carrying `tool_result` blocks,
|
|
598
|
-
* so we collapse consecutive tool-only
|
|
598
|
+
* so we collapse consecutive tool-only V4 messages into the surrounding user
|
|
599
599
|
* turn shape.
|
|
600
600
|
*/
|
|
601
601
|
export function translatePromptToAnthropic(prompt) {
|
|
@@ -669,7 +669,7 @@ export function translatePromptToAnthropic(prompt) {
|
|
|
669
669
|
return system !== undefined ? { system, messages } : { messages };
|
|
670
670
|
}
|
|
671
671
|
/**
|
|
672
|
-
* Translates
|
|
672
|
+
* Translates V4 function-tool definitions into Anthropic's `Tool[]` shape.
|
|
673
673
|
* Skips non-function variants (provider-defined / dynamic) — the pass-through
|
|
674
674
|
* path supports only the function-tool subset that Mastra's tool mapper produces.
|
|
675
675
|
*/
|
|
@@ -685,13 +685,11 @@ export function translateToolsToAnthropic(options) {
|
|
|
685
685
|
}));
|
|
686
686
|
}
|
|
687
687
|
function toAnthropicFileBlock(part) {
|
|
688
|
-
|
|
689
|
-
throw new Error(`Unsupported file data for "${part.mediaType}": expected a base64 string, got ${part.data instanceof URL ? 'a URL' : 'binary data'}. Only base64-encoded file data is supported.`);
|
|
690
|
-
}
|
|
688
|
+
const base64 = v4FileDataToBase64(part.data, part.mediaType);
|
|
691
689
|
if (part.mediaType === 'application/pdf') {
|
|
692
690
|
return {
|
|
693
691
|
type: 'document',
|
|
694
|
-
source: { type: 'base64', media_type: 'application/pdf', data:
|
|
692
|
+
source: { type: 'base64', media_type: 'application/pdf', data: base64 },
|
|
695
693
|
};
|
|
696
694
|
}
|
|
697
695
|
if (part.mediaType === 'image/png' ||
|
|
@@ -700,7 +698,7 @@ function toAnthropicFileBlock(part) {
|
|
|
700
698
|
part.mediaType === 'image/webp') {
|
|
701
699
|
return {
|
|
702
700
|
type: 'image',
|
|
703
|
-
source: { type: 'base64', media_type: part.mediaType, data:
|
|
701
|
+
source: { type: 'base64', media_type: part.mediaType, data: base64 },
|
|
704
702
|
};
|
|
705
703
|
}
|
|
706
704
|
throw new Error(`Unsupported file media type "${part.mediaType}". Anthropic supports image/png, image/jpeg, image/gif, image/webp, application/pdf.`);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ChatCompletionMessageParam, ChatCompletionTool } from 'openai/resources/chat/completions';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4FinishReason, LanguageModelV4Prompt } from '@ai-sdk/provider';
|
|
3
3
|
import type { LogBus } from '@salesforce/agentic-common';
|
|
4
4
|
import type { ModelConnectivityInfo, WireCommunicationEvent } from '@salesforce/sfdx-agent-sdk';
|
|
5
|
-
import { type GatewayFetchWireHooks } from './
|
|
5
|
+
import { type GatewayFetchWireHooks } from './stream-mapping.js';
|
|
6
6
|
/**
|
|
7
|
-
* Builds the in-process `
|
|
7
|
+
* Builds the in-process `LanguageModelV4` impl that drives the `openai` SDK's
|
|
8
8
|
* `chat.completions.create({ stream: true })` against an OpenAI-compatible
|
|
9
9
|
* endpoint (LLM Gateway Express, LiteLLM, vLLM, etc. — anything that speaks
|
|
10
10
|
* `/v1/chat/completions`).
|
|
@@ -15,7 +15,7 @@ import { type GatewayFetchWireHooks } from './v3-stream-mapping.js';
|
|
|
15
15
|
* `input` split); (3) streaming events are `ChatCompletionChunk` objects with
|
|
16
16
|
* `choices[0].delta` text/tool-call deltas and the terminal `finish_reason`
|
|
17
17
|
* landing on the same chunk shape rather than a discrete `response.completed`
|
|
18
|
-
* event. The
|
|
18
|
+
* event. The V4 mapping output is identical — same `text-delta` / `tool-call` /
|
|
19
19
|
* `finish` parts the Responses builder produces.
|
|
20
20
|
*
|
|
21
21
|
* Per-call freshness contract is the same as the other builders: every HTTP
|
|
@@ -35,28 +35,28 @@ export declare function buildOpenAIChatCompletionsLanguageModel(opts: {
|
|
|
35
35
|
/** See `buildAnthropicLanguageModel` `getConsumerToolSchemas`. */
|
|
36
36
|
getConsumerToolSchemas?: () => ReadonlyMap<string, Record<string, unknown>>;
|
|
37
37
|
/** See `buildAnthropicLanguageModel` `getToolArgsRetryCounts`. */
|
|
38
|
-
getToolArgsRetryCounts?: (options:
|
|
38
|
+
getToolArgsRetryCounts?: (options: LanguageModelV4CallOptions) => Map<string, number>;
|
|
39
39
|
/** Per-read idle timeout (ms) for the gateway response body; see `createGatewayFetch`. */
|
|
40
40
|
idleReadTimeoutMs?: number;
|
|
41
|
-
}):
|
|
41
|
+
}): LanguageModelV4;
|
|
42
42
|
/**
|
|
43
|
-
* Translates a
|
|
43
|
+
* Translates a V4 `LanguageModelV4Prompt` into the OpenAI Chat Completions
|
|
44
44
|
* `messages` array shape. System messages stay as `system` role; user / tool
|
|
45
45
|
* follow naturally. Assistant text + tool-calls collapse onto a single
|
|
46
46
|
* assistant message carrying a `content` string (when text is present) plus
|
|
47
47
|
* `tool_calls` (when calls are present). Tool results land on `tool` role
|
|
48
48
|
* with `tool_call_id`.
|
|
49
49
|
*/
|
|
50
|
-
export declare function translatePromptToChatCompletions(prompt:
|
|
50
|
+
export declare function translatePromptToChatCompletions(prompt: LanguageModelV4Prompt): ChatCompletionMessageParam[];
|
|
51
51
|
/**
|
|
52
|
-
* Translates
|
|
52
|
+
* Translates V4 function-tool definitions into the Chat Completions `tools[]`
|
|
53
53
|
* shape. Skips non-function variants — same posture as the Responses builder.
|
|
54
54
|
*/
|
|
55
|
-
export declare function translateToolsToChatCompletions(options:
|
|
55
|
+
export declare function translateToolsToChatCompletions(options: LanguageModelV4CallOptions): ChatCompletionTool[];
|
|
56
56
|
/**
|
|
57
|
-
* Maps Chat Completions `finish_reason` onto
|
|
57
|
+
* Maps Chat Completions `finish_reason` onto V4's unified shape. The Chat
|
|
58
58
|
* Completions API uses `'stop' | 'length' | 'tool_calls' | 'content_filter' |
|
|
59
59
|
* 'function_call' | null`. `function_call` is the legacy shape from before
|
|
60
60
|
* `tool_calls` existed; modern endpoints emit `'tool_calls'`.
|
|
61
61
|
*/
|
|
62
|
-
export declare function mapChatCompletionFinishReason(raw: string | null, sawToolCalls: boolean):
|
|
62
|
+
export declare function mapChatCompletionFinishReason(raw: string | null, sawToolCalls: boolean): LanguageModelV4FinishReason;
|