@prismshadow/mmsp 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -0
- package/dist/ant_messages/client.d.ts +59 -0
- package/dist/ant_messages/client.d.ts.map +1 -0
- package/dist/ant_messages/client.js +419 -0
- package/dist/ant_messages/index.d.ts +2 -0
- package/dist/ant_messages/index.d.ts.map +1 -0
- package/dist/ant_messages/index.js +18 -0
- package/dist/anthropic_official/client.d.ts +63 -0
- package/dist/anthropic_official/client.d.ts.map +1 -0
- package/dist/anthropic_official/client.js +540 -0
- package/dist/anthropic_official/index.d.ts +2 -0
- package/dist/anthropic_official/index.d.ts.map +1 -0
- package/dist/anthropic_official/index.js +18 -0
- package/dist/autoClient.d.ts +96 -0
- package/dist/autoClient.d.ts.map +1 -0
- package/dist/autoClient.js +258 -0
- package/dist/baseClient.d.ts +111 -0
- package/dist/baseClient.d.ts.map +1 -0
- package/dist/baseClient.js +284 -0
- package/dist/deepseek_official/client.d.ts +60 -0
- package/dist/deepseek_official/client.d.ts.map +1 -0
- package/dist/deepseek_official/client.js +407 -0
- package/dist/deepseek_official/index.d.ts +2 -0
- package/dist/deepseek_official/index.d.ts.map +1 -0
- package/dist/deepseek_official/index.js +18 -0
- package/dist/errors.d.ts +84 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +140 -0
- package/dist/gemini_generate_content/client.d.ts +86 -0
- package/dist/gemini_generate_content/client.d.ts.map +1 -0
- package/dist/gemini_generate_content/client.js +809 -0
- package/dist/gemini_generate_content/index.d.ts +2 -0
- package/dist/gemini_generate_content/index.d.ts.map +1 -0
- package/dist/gemini_generate_content/index.js +18 -0
- package/dist/gemini_official/client.d.ts +87 -0
- package/dist/gemini_official/client.d.ts.map +1 -0
- package/dist/gemini_official/client.js +780 -0
- package/dist/gemini_official/index.d.ts +2 -0
- package/dist/gemini_official/index.d.ts.map +1 -0
- package/dist/gemini_official/index.js +18 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/integration/index.d.ts +1 -0
- package/dist/integration/index.d.ts.map +1 -0
- package/dist/integration/index.js +14 -0
- package/dist/integration/playground.d.ts +17 -0
- package/dist/integration/playground.d.ts.map +1 -0
- package/dist/integration/playground.js +3246 -0
- package/dist/integration/tracer.d.ts +188 -0
- package/dist/integration/tracer.d.ts.map +1 -0
- package/dist/integration/tracer.js +1928 -0
- package/dist/legacy.d.ts +13 -0
- package/dist/legacy.d.ts.map +1 -0
- package/dist/legacy.js +59 -0
- package/dist/minimax_official/client.d.ts +43 -0
- package/dist/minimax_official/client.d.ts.map +1 -0
- package/dist/minimax_official/client.js +346 -0
- package/dist/minimax_official/index.d.ts +2 -0
- package/dist/minimax_official/index.d.ts.map +1 -0
- package/dist/minimax_official/index.js +18 -0
- package/dist/moonshot_official/client.d.ts +73 -0
- package/dist/moonshot_official/client.d.ts.map +1 -0
- package/dist/moonshot_official/client.js +477 -0
- package/dist/moonshot_official/index.d.ts +2 -0
- package/dist/moonshot_official/index.d.ts.map +1 -0
- package/dist/moonshot_official/index.js +18 -0
- package/dist/openai_chat/client.d.ts +67 -0
- package/dist/openai_chat/client.d.ts.map +1 -0
- package/dist/openai_chat/client.js +419 -0
- package/dist/openai_chat/index.d.ts +2 -0
- package/dist/openai_chat/index.d.ts.map +1 -0
- package/dist/openai_chat/index.js +18 -0
- package/dist/openai_chat_vllm_adapter/client.d.ts +15 -0
- package/dist/openai_chat_vllm_adapter/client.d.ts.map +1 -0
- package/dist/openai_chat_vllm_adapter/client.js +118 -0
- package/dist/openai_chat_vllm_adapter/index.d.ts +2 -0
- package/dist/openai_chat_vllm_adapter/index.d.ts.map +1 -0
- package/dist/openai_chat_vllm_adapter/index.js +5 -0
- package/dist/openai_embedding/client.d.ts +46 -0
- package/dist/openai_embedding/client.d.ts.map +1 -0
- package/dist/openai_embedding/client.js +124 -0
- package/dist/openai_embedding/index.d.ts +2 -0
- package/dist/openai_embedding/index.d.ts.map +1 -0
- package/dist/openai_embedding/index.js +18 -0
- package/dist/openai_official/client.d.ts +61 -0
- package/dist/openai_official/client.d.ts.map +1 -0
- package/dist/openai_official/client.js +464 -0
- package/dist/openai_official/index.d.ts +2 -0
- package/dist/openai_official/index.d.ts.map +1 -0
- package/dist/openai_official/index.js +18 -0
- package/dist/openai_responses/client.d.ts +61 -0
- package/dist/openai_responses/client.d.ts.map +1 -0
- package/dist/openai_responses/client.js +449 -0
- package/dist/openai_responses/index.d.ts +2 -0
- package/dist/openai_responses/index.d.ts.map +1 -0
- package/dist/openai_responses/index.js +18 -0
- package/dist/registry.d.ts +49 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +798 -0
- package/dist/streamItems.d.ts +36 -0
- package/dist/streamItems.d.ts.map +1 -0
- package/dist/streamItems.js +183 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +37 -0
- package/dist/utils.d.ts +99 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +312 -0
- package/dist/zai_official/client.d.ts +78 -0
- package/dist/zai_official/client.d.ts.map +1 -0
- package/dist/zai_official/client.js +420 -0
- package/dist/zai_official/index.d.ts +2 -0
- package/dist/zai_official/index.d.ts.map +1 -0
- package/dist/zai_official/index.js +18 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# MMSP TypeScript Implementation
|
|
2
|
+
|
|
3
|
+
This directory contains the TypeScript implementation of MMSP, mirroring the Python implementation in `src_py/`.
|
|
4
|
+
|
|
5
|
+
## Building
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
make install # Install dependencies
|
|
9
|
+
make build # Build TypeScript to JavaScript
|
|
10
|
+
make lint # Run ESLint
|
|
11
|
+
make test # Run tests
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Basic Client Usage
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { AutoLLMClient } from "@prismshadow/mmsp";
|
|
20
|
+
|
|
21
|
+
process.env.OPENAI_API_KEY = "your-openai-api-key";
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
// The official OpenAI client, named by the model id's family
|
|
25
|
+
const client = new AutoLLMClient({ model: "gpt-5.5" });
|
|
26
|
+
// The same, spelled out, with the key given in code:
|
|
27
|
+
// const client = new AutoLLMClient({ model: "gpt-5.5", clientType: "openai-official", apiKey: "your-openai-api-key" });
|
|
28
|
+
// A compatible client, for any endpoint that serves OpenAI Chat Completions:
|
|
29
|
+
// const client = new AutoLLMClient({ model: "custom-model", clientType: "openai-chat", baseUrl: "http://127.0.0.1:8000/v1/", apiKey: "none" });
|
|
30
|
+
// For Gemini on Google Vertex AI, the service-account JSON key is the API key:
|
|
31
|
+
// const client = new AutoLLMClient({ model: "gemini-3.8-flash", apiKey: fs.readFileSync("service-account.json", "utf8") });
|
|
32
|
+
|
|
33
|
+
for await (const event of client.streamingResponseStateful({
|
|
34
|
+
message: {
|
|
35
|
+
role: "user",
|
|
36
|
+
content_items: [{ type: "text.done", text: "Hello!" }],
|
|
37
|
+
},
|
|
38
|
+
config: {},
|
|
39
|
+
})) {
|
|
40
|
+
console.log(event);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
main().catch(console.error);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`clientType` names one of the official clients (`openai-official`, `anthropic-official`, `gemini-official`, `zai-official`, `moonshot-official`, `deepseek-official`, `minimax-official`) or one of the compatible clients (`openai-responses`, `openai-chat`, `openai-chat-vllm-adapter`, `openai-embedding`, `ant-messages`, `gemini-generate-content`). It may be omitted for a model id that begins with a known family (`gpt-`, `text-embedding-`, `claude-`, `gemini-`, `glm-`, `kimi-`, `deepseek-`, `minimax-`), which names its official client; any other id throws and asks for one.
|
|
48
|
+
|
|
49
|
+
A Vertex AI service-account key is served through generateContent, because Vertex AI's Interactions endpoint serves none of the Gemini models; any other Gemini key uses the Interactions API. `clientType: "gemini-generate-content"` names generateContent explicitly, for gateways that proxy it.
|
|
50
|
+
|
|
51
|
+
Both streaming methods yield `delta` events, each carrying exactly one content item, then exactly one `stop` event, always last, carrying `usage_metadata` and `finish_reason`. Each item streams as one or more `.delta` fragments (`text.delta`, `tool_call.delta`, …) followed by its complete `.done` item (`text.done`, `tool_call.done`, …); items never interleave.
|
|
52
|
+
|
|
53
|
+
### History Management
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Get current history
|
|
57
|
+
const history = client.getHistory();
|
|
58
|
+
|
|
59
|
+
// Clear all history
|
|
60
|
+
client.clearHistory();
|
|
61
|
+
|
|
62
|
+
// Replace history with a saved copy
|
|
63
|
+
client.setHistory(history);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Messages hold complete items only, typed with a `.done` suffix. Item types without the suffix, saved before 0.5.0, are still accepted with a deprecation warning until 0.6.0; `normalizeLegacyMessages(messages)` converts stored messages.
|
|
67
|
+
|
|
68
|
+
### Tracer Usage
|
|
69
|
+
|
|
70
|
+
Save and browse conversation history with a web interface:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { Tracer } from "@prismshadow/mmsp/integration/tracer";
|
|
74
|
+
|
|
75
|
+
// Create a tracer instance
|
|
76
|
+
const tracer = new Tracer("./cache");
|
|
77
|
+
|
|
78
|
+
// Save conversation history
|
|
79
|
+
const model = "gpt-5.5";
|
|
80
|
+
const history = [
|
|
81
|
+
{ role: "user", content_items: [{ type: "text.done", text: "Hello!" }] },
|
|
82
|
+
{
|
|
83
|
+
role: "assistant",
|
|
84
|
+
content_items: [{ type: "text.done", text: "Hi there!" }],
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
const config = {};
|
|
88
|
+
tracer.saveHistory(model, history, "session/conv_001", config);
|
|
89
|
+
|
|
90
|
+
// Start web server to view saved conversations
|
|
91
|
+
tracer.startWebServer("127.0.0.1", 25750);
|
|
92
|
+
// Open http://127.0.0.1:25750 in your browser
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Playground Usage
|
|
96
|
+
|
|
97
|
+
Interactive web interface for chatting with LLMs:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { startPlaygroundServer } from "@prismshadow/mmsp/integration/playground";
|
|
101
|
+
|
|
102
|
+
// Start the playground server
|
|
103
|
+
startPlaygroundServer("127.0.0.1", 25751);
|
|
104
|
+
// Open http://127.0.0.1:25751 in your browser
|
|
105
|
+
// Open http://127.0.0.1:25751/tracer/ to browse traces
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Examples
|
|
109
|
+
|
|
110
|
+
Run the examples:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Build the project
|
|
114
|
+
npm run build
|
|
115
|
+
|
|
116
|
+
# Run tracer example
|
|
117
|
+
npm run tracer
|
|
118
|
+
|
|
119
|
+
# Run playground example
|
|
120
|
+
npm run playground
|
|
121
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { BetaMessageParam, BetaRawMessageStreamEvent } from "@anthropic-ai/sdk/resources/beta/messages";
|
|
2
|
+
import { LLMClient } from "../baseClient";
|
|
3
|
+
import { UniConfig, UniEvent, UniMessage } from "../types";
|
|
4
|
+
/**
|
|
5
|
+
* Anthropic Messages-compatible client implementation.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AntMessagesClient extends LLMClient {
|
|
8
|
+
protected _model: string;
|
|
9
|
+
private _client;
|
|
10
|
+
/**
|
|
11
|
+
* Initialize Anthropic Messages-compatible client with model, API key, and base URL.
|
|
12
|
+
*/
|
|
13
|
+
constructor(options: {
|
|
14
|
+
model: string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
baseUrl?: string | null;
|
|
17
|
+
defaultHeaders?: Record<string, string>;
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Convert image URL to an Anthropic image source block.
|
|
21
|
+
*/
|
|
22
|
+
private _convertImageUrlToSource;
|
|
23
|
+
/**
|
|
24
|
+
* Convert ThinkingLevel enum to the Messages API thinking config.
|
|
25
|
+
*/
|
|
26
|
+
private _convertThinkingLevelToThinkingConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Convert ToolChoice to the Messages API tool_choice format.
|
|
29
|
+
*/
|
|
30
|
+
private _convertToolChoice;
|
|
31
|
+
/**
|
|
32
|
+
* Transform universal configuration to Anthropic Messages-compatible configuration.
|
|
33
|
+
*/
|
|
34
|
+
transformUniConfigToModelConfig(config: UniConfig): any;
|
|
35
|
+
/**
|
|
36
|
+
* Transform universal message format to the Messages API BetaMessageParam format.
|
|
37
|
+
*/
|
|
38
|
+
transformUniMessageToModelInput(messages: UniMessage[], _signal?: AbortSignal): BetaMessageParam[];
|
|
39
|
+
/**
|
|
40
|
+
* Transform one Messages API stream event into a universal event, identifying items by
|
|
41
|
+
* content block index.
|
|
42
|
+
*/
|
|
43
|
+
transformModelOutputToUniEvent(modelOutput: BetaRawMessageStreamEvent): UniEvent;
|
|
44
|
+
/**
|
|
45
|
+
* Stream generate using an Anthropic Messages-compatible API with unified conversion methods.
|
|
46
|
+
*/
|
|
47
|
+
_streamingResponseInternal(options: {
|
|
48
|
+
messages: UniMessage[];
|
|
49
|
+
config: UniConfig;
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
}): AsyncGenerator<UniEvent>;
|
|
52
|
+
/**
|
|
53
|
+
* List the model ids the configured endpoint serves.
|
|
54
|
+
*
|
|
55
|
+
* @returns The model ids, in the order the endpoint returned them.
|
|
56
|
+
*/
|
|
57
|
+
listModels(): Promise<string[]>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/ant_messages/client.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EAC1B,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAOL,SAAS,EACT,QAAQ,EACR,UAAU,EAEX,MAAM,UAAU,CAAC;AASlB;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,OAAO,CAAY;IAE3B;;OAEG;gBACS,OAAO,EAAE;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACzC;IAoBD;;OAEG;IAEH,OAAO,CAAC,wBAAwB;IAgBhC;;OAEG;IACH,OAAO,CAAC,qCAAqC;IAgC7C;;OAEG;IAEH,OAAO,CAAC,kBAAkB;IAoB1B;;OAEG;IAEH,+BAA+B,CAAC,MAAM,EAAE,SAAS,GAAG,GAAG;IA2EvD;;OAEG;IACH,+BAA+B,CAC7B,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,CAAC,EAAE,WAAW,GACpB,gBAAgB,EAAE;IAmErB;;;OAGG;IACH,8BAA8B,CAC5B,WAAW,EAAE,yBAAyB,GACrC,QAAQ;IAoIX;;OAEG;IACI,0BAA0B,CAAC,OAAO,EAAE;QACzC,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,MAAM,EAAE,SAAS,CAAC;QAClB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,cAAc,CAAC,QAAQ,CAAC;IAsB5B;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAQtC"}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2025 Prism Shadow. and/or its affiliates
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.AntMessagesClient = void 0;
|
|
20
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
21
|
+
const baseClient_1 = require("../baseClient");
|
|
22
|
+
const errors_1 = require("../errors");
|
|
23
|
+
const types_1 = require("../types");
|
|
24
|
+
const utils_1 = require("../utils");
|
|
25
|
+
const REDACTED_THINKING = "_REDACTED_THINKING";
|
|
26
|
+
/**
|
|
27
|
+
* Anthropic Messages-compatible client implementation.
|
|
28
|
+
*/
|
|
29
|
+
class AntMessagesClient extends baseClient_1.LLMClient {
|
|
30
|
+
/**
|
|
31
|
+
* Initialize Anthropic Messages-compatible client with model, API key, and base URL.
|
|
32
|
+
*/
|
|
33
|
+
constructor(options) {
|
|
34
|
+
super();
|
|
35
|
+
this._model = options.model;
|
|
36
|
+
const { apiKey: key, baseUrl: url } = (0, utils_1.resolveCredentials)(this.constructor.name, options, { key: "ANTHROPIC_API_KEY", baseUrl: "ANTHROPIC_BASE_URL" });
|
|
37
|
+
// send the credential through both header conventions: Anthropic and DeepSeek read
|
|
38
|
+
// x-api-key while gateways such as OpenRouter and Z.AI read Authorization: Bearer. With no
|
|
39
|
+
// credential the token has to be null, not undefined, or the SDK fills it from
|
|
40
|
+
// ANTHROPIC_AUTH_TOKEN.
|
|
41
|
+
this._client = new sdk_1.default({
|
|
42
|
+
apiKey: key,
|
|
43
|
+
authToken: key ?? null,
|
|
44
|
+
baseURL: url,
|
|
45
|
+
defaultHeaders: options.defaultHeaders,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Convert image URL to an Anthropic image source block.
|
|
50
|
+
*/
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
_convertImageUrlToSource(url) {
|
|
53
|
+
if (url.startsWith("data:")) {
|
|
54
|
+
const match = url.match(/^data:([^;]+);base64,(.+)$/);
|
|
55
|
+
if (!match) {
|
|
56
|
+
throw new Error(`Invalid base64 image: ${url}`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
type: "image",
|
|
60
|
+
source: { type: "base64", media_type: match[1], data: match[2] },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { type: "image", source: { type: "url", url } };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convert ThinkingLevel enum to the Messages API thinking config.
|
|
67
|
+
*/
|
|
68
|
+
_convertThinkingLevelToThinkingConfig(thinkingLevel) {
|
|
69
|
+
// NONE is explicit rather than omitted because some servers (e.g. Z.AI) think by default
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
const mapping = {
|
|
72
|
+
[types_1.ThinkingLevel.NONE]: { thinking: { type: "disabled" } },
|
|
73
|
+
[types_1.ThinkingLevel.LOW]: {
|
|
74
|
+
thinking: { type: "adaptive" },
|
|
75
|
+
output_config: { effort: "low" },
|
|
76
|
+
},
|
|
77
|
+
[types_1.ThinkingLevel.MEDIUM]: {
|
|
78
|
+
thinking: { type: "adaptive" },
|
|
79
|
+
output_config: { effort: "medium" },
|
|
80
|
+
},
|
|
81
|
+
[types_1.ThinkingLevel.HIGH]: {
|
|
82
|
+
thinking: { type: "adaptive" },
|
|
83
|
+
output_config: { effort: "high" },
|
|
84
|
+
},
|
|
85
|
+
[types_1.ThinkingLevel.XHIGH]: {
|
|
86
|
+
thinking: { type: "adaptive" },
|
|
87
|
+
output_config: { effort: "xhigh" },
|
|
88
|
+
},
|
|
89
|
+
[types_1.ThinkingLevel.MAX]: {
|
|
90
|
+
thinking: { type: "adaptive" },
|
|
91
|
+
output_config: { effort: "max" },
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
return mapping[thinkingLevel];
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Convert ToolChoice to the Messages API tool_choice format.
|
|
98
|
+
*/
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
_convertToolChoice(toolChoice) {
|
|
101
|
+
if (Array.isArray(toolChoice)) {
|
|
102
|
+
if (toolChoice.length > 1) {
|
|
103
|
+
throw new errors_1.UnsupportedParameterError({
|
|
104
|
+
client: this.constructor.name,
|
|
105
|
+
parameter: "tool_choice",
|
|
106
|
+
message: "The Messages API does not support multiple tool choices.",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return { type: "tool", name: toolChoice[0] };
|
|
110
|
+
}
|
|
111
|
+
else if (toolChoice === "none") {
|
|
112
|
+
return { type: "none" };
|
|
113
|
+
}
|
|
114
|
+
else if (toolChoice === "auto") {
|
|
115
|
+
return { type: "auto" };
|
|
116
|
+
}
|
|
117
|
+
else if (toolChoice === "required") {
|
|
118
|
+
return { type: "any" };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Transform universal configuration to Anthropic Messages-compatible configuration.
|
|
123
|
+
*/
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
transformUniConfigToModelConfig(config) {
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
127
|
+
const antConfig = { model: this._model, stream: true };
|
|
128
|
+
if (config.system_prompt !== undefined) {
|
|
129
|
+
antConfig.system = config.system_prompt;
|
|
130
|
+
}
|
|
131
|
+
if (config.max_tokens !== undefined) {
|
|
132
|
+
antConfig.max_tokens = config.max_tokens;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// the Messages API requires max_tokens to be specified
|
|
136
|
+
antConfig.max_tokens = 64000;
|
|
137
|
+
}
|
|
138
|
+
if (config.temperature !== undefined) {
|
|
139
|
+
antConfig.temperature = config.temperature;
|
|
140
|
+
}
|
|
141
|
+
if (config.thinking_level !== undefined) {
|
|
142
|
+
Object.assign(antConfig, this._convertThinkingLevelToThinkingConfig(config.thinking_level));
|
|
143
|
+
}
|
|
144
|
+
if (config.thinking_summary !== undefined) {
|
|
145
|
+
// display lives on the thinking block, so a summary asked for on its own selects
|
|
146
|
+
// adaptive thinking. A disabled block is the one place it cannot ride along --
|
|
147
|
+
// "thinking.disabled.display: Extra inputs are not permitted" (400, verified live
|
|
148
|
+
// 2026-09-03) -- and thinking_level NONE disables thinking, leaving nothing to show.
|
|
149
|
+
antConfig.thinking = antConfig.thinking ?? { type: "adaptive" };
|
|
150
|
+
if (antConfig.thinking.type !== "disabled") {
|
|
151
|
+
antConfig.thinking.display = config.thinking_summary
|
|
152
|
+
? "summarized"
|
|
153
|
+
: "omitted";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Convert tools to the Messages API tool schema
|
|
157
|
+
if (config.tools !== undefined) {
|
|
158
|
+
antConfig.tools = config.tools.map((tool) => {
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
160
|
+
const antTool = {};
|
|
161
|
+
for (const [key, value] of Object.entries(tool)) {
|
|
162
|
+
antTool[key.replace("parameters", "input_schema")] = value;
|
|
163
|
+
}
|
|
164
|
+
return antTool;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
// Convert tool_choice
|
|
168
|
+
if (config.tool_choice !== undefined) {
|
|
169
|
+
antConfig.tool_choice = this._convertToolChoice(config.tool_choice);
|
|
170
|
+
}
|
|
171
|
+
if (config.fast_mode) {
|
|
172
|
+
antConfig.speed = "fast";
|
|
173
|
+
antConfig.betas = ["fast-mode-2026-02-01"];
|
|
174
|
+
}
|
|
175
|
+
if (config.prompt_caching !== undefined &&
|
|
176
|
+
config.prompt_caching !== types_1.PromptCaching.ENABLE) {
|
|
177
|
+
throw new errors_1.UnsupportedParameterError({
|
|
178
|
+
client: this.constructor.name,
|
|
179
|
+
parameter: "prompt_caching",
|
|
180
|
+
message: "prompt_caching must be ENABLE for the Messages API.",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return antConfig;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Transform universal message format to the Messages API BetaMessageParam format.
|
|
187
|
+
*/
|
|
188
|
+
transformUniMessageToModelInput(messages, _signal) {
|
|
189
|
+
const antMessages = [];
|
|
190
|
+
for (const msg of messages) {
|
|
191
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
192
|
+
const contentBlocks = [];
|
|
193
|
+
for (const item of msg.content_items) {
|
|
194
|
+
if (item.type === "text.done") {
|
|
195
|
+
contentBlocks.push({ type: "text", text: item.text });
|
|
196
|
+
}
|
|
197
|
+
else if (item.type === "image_url.done") {
|
|
198
|
+
contentBlocks.push(this._convertImageUrlToSource(item.image_url));
|
|
199
|
+
}
|
|
200
|
+
else if (item.type === "thinking.done") {
|
|
201
|
+
if (item.thinking === REDACTED_THINKING) {
|
|
202
|
+
contentBlocks.push({
|
|
203
|
+
type: "redacted_thinking",
|
|
204
|
+
data: item.fidelity?.signature,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// third-party servers accept thinking without a signature, but the
|
|
209
|
+
// official API requires the one it emitted
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
211
|
+
const thinkingBlock = {
|
|
212
|
+
type: "thinking",
|
|
213
|
+
thinking: item.thinking,
|
|
214
|
+
};
|
|
215
|
+
if (item.fidelity?.signature != null) {
|
|
216
|
+
thinkingBlock.signature = item.fidelity.signature;
|
|
217
|
+
}
|
|
218
|
+
contentBlocks.push(thinkingBlock);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (item.type === "tool_call.done") {
|
|
222
|
+
contentBlocks.push({
|
|
223
|
+
type: "tool_use",
|
|
224
|
+
id: item.tool_call_id,
|
|
225
|
+
name: item.name,
|
|
226
|
+
input: item.arguments,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else if (item.type === "tool_result.done") {
|
|
230
|
+
if (!item.tool_call_id) {
|
|
231
|
+
throw new Error("tool_call_id is required for tool result.");
|
|
232
|
+
}
|
|
233
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
234
|
+
const toolResult = [{ type: "text", text: item.text }];
|
|
235
|
+
if (item.images) {
|
|
236
|
+
for (const imageUrl of item.images) {
|
|
237
|
+
toolResult.push(this._convertImageUrlToSource(imageUrl));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
contentBlocks.push({
|
|
241
|
+
type: "tool_result",
|
|
242
|
+
content: toolResult,
|
|
243
|
+
tool_use_id: item.tool_call_id,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
throw new Error(`Unknown item: ${JSON.stringify(item)}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
antMessages.push({ role: msg.role, content: contentBlocks });
|
|
251
|
+
}
|
|
252
|
+
return antMessages;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Transform one Messages API stream event into a universal event, identifying items by
|
|
256
|
+
* content block index.
|
|
257
|
+
*/
|
|
258
|
+
transformModelOutputToUniEvent(modelOutput) {
|
|
259
|
+
let eventType = "delta";
|
|
260
|
+
const contentItems = [];
|
|
261
|
+
let usageMetadata = null;
|
|
262
|
+
let finishReason = null;
|
|
263
|
+
const antEventType = modelOutput.type;
|
|
264
|
+
if (antEventType === "content_block_start") {
|
|
265
|
+
const itemId = String(modelOutput.index);
|
|
266
|
+
const block = modelOutput.content_block;
|
|
267
|
+
if (block.type === "tool_use") {
|
|
268
|
+
contentItems.push({
|
|
269
|
+
type: "tool_call.delta",
|
|
270
|
+
name: block.name,
|
|
271
|
+
arguments: "",
|
|
272
|
+
tool_call_id: block.id,
|
|
273
|
+
fidelity: { item_id: itemId },
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
else if (block.type === "redacted_thinking") {
|
|
277
|
+
contentItems.push({
|
|
278
|
+
type: "thinking.delta",
|
|
279
|
+
thinking: REDACTED_THINKING,
|
|
280
|
+
fidelity: { item_id: itemId, signature: block.data },
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else if (antEventType === "content_block_delta") {
|
|
285
|
+
const itemId = String(modelOutput.index);
|
|
286
|
+
const delta = modelOutput.delta;
|
|
287
|
+
if (delta.type === "thinking_delta") {
|
|
288
|
+
contentItems.push({
|
|
289
|
+
type: "thinking.delta",
|
|
290
|
+
thinking: delta.thinking,
|
|
291
|
+
fidelity: { item_id: itemId },
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
else if (delta.type === "text_delta") {
|
|
295
|
+
contentItems.push({
|
|
296
|
+
type: "text.delta",
|
|
297
|
+
text: delta.text,
|
|
298
|
+
fidelity: { item_id: itemId },
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
else if (delta.type === "input_json_delta") {
|
|
302
|
+
contentItems.push({
|
|
303
|
+
type: "tool_call.delta",
|
|
304
|
+
name: "",
|
|
305
|
+
arguments: delta.partial_json,
|
|
306
|
+
tool_call_id: "",
|
|
307
|
+
fidelity: { item_id: itemId },
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
else if (delta.type === "signature_delta") {
|
|
311
|
+
// the last delta of a thinking block: its signature
|
|
312
|
+
contentItems.push({
|
|
313
|
+
type: "thinking.delta",
|
|
314
|
+
thinking: "",
|
|
315
|
+
fidelity: { item_id: itemId, signature: delta.signature },
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else if (antEventType === "message_start") {
|
|
320
|
+
eventType = "stop";
|
|
321
|
+
const usage = modelOutput.message.usage;
|
|
322
|
+
if (usage) {
|
|
323
|
+
const cacheCreationTokens = usage.cache_creation_input_tokens || 0;
|
|
324
|
+
usageMetadata = {
|
|
325
|
+
cached_tokens: usage.cache_read_input_tokens,
|
|
326
|
+
prompt_tokens: usage.input_tokens + cacheCreationTokens,
|
|
327
|
+
thoughts_tokens: null,
|
|
328
|
+
response_tokens: null,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else if (antEventType === "message_delta") {
|
|
333
|
+
eventType = "stop";
|
|
334
|
+
const stopReasonMapping = {
|
|
335
|
+
end_turn: "stop",
|
|
336
|
+
max_tokens: "length",
|
|
337
|
+
stop_sequence: "stop",
|
|
338
|
+
tool_use: "tool_call",
|
|
339
|
+
};
|
|
340
|
+
const stopReason = modelOutput.delta.stop_reason;
|
|
341
|
+
if (stopReason) {
|
|
342
|
+
finishReason = stopReasonMapping[stopReason] || "unknown";
|
|
343
|
+
}
|
|
344
|
+
const usage = modelOutput.usage;
|
|
345
|
+
if (usage) {
|
|
346
|
+
// gateways report zero usage in message_start and the full counts here, so the
|
|
347
|
+
// delta also carries the input-side fields (null on servers that omit them)
|
|
348
|
+
const promptTokens = usage.input_tokens != null
|
|
349
|
+
? usage.input_tokens + (usage.cache_creation_input_tokens || 0)
|
|
350
|
+
: null;
|
|
351
|
+
const thinkingTokens =
|
|
352
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
353
|
+
usage.output_tokens_details?.thinking_tokens ?? null;
|
|
354
|
+
usageMetadata = (0, utils_1.fixOpenrouterUsageMetadata)({
|
|
355
|
+
cached_tokens: usage.cache_read_input_tokens ?? null,
|
|
356
|
+
prompt_tokens: promptTokens,
|
|
357
|
+
thoughts_tokens: thinkingTokens,
|
|
358
|
+
response_tokens: usage.output_tokens - (thinkingTokens || 0),
|
|
359
|
+
}, this._client.baseURL);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
else if ([
|
|
363
|
+
"content_block_stop",
|
|
364
|
+
"message_stop",
|
|
365
|
+
"text",
|
|
366
|
+
"thinking",
|
|
367
|
+
"signature",
|
|
368
|
+
"input_json",
|
|
369
|
+
"ping",
|
|
370
|
+
].includes(antEventType)) {
|
|
371
|
+
// a block needs no stop: it is done when the next one begins or the stream ends. The SDK
|
|
372
|
+
// drops the "ping" heartbeat at the SSE layer; it reaches here only from gateways that
|
|
373
|
+
// relabel it onto another event
|
|
374
|
+
}
|
|
375
|
+
else if ((0, utils_1.isDebugEnabled)()) {
|
|
376
|
+
throw new Error(`Unknown output: ${JSON.stringify(modelOutput)}`);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// a gateway injects its own events (heartbeats, cost tickers) into the stream, and
|
|
380
|
+
// killing a long generation over one costs more than dropping it
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
role: "assistant",
|
|
384
|
+
event_type: eventType,
|
|
385
|
+
content_items: contentItems,
|
|
386
|
+
usage_metadata: usageMetadata,
|
|
387
|
+
finish_reason: finishReason,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Stream generate using an Anthropic Messages-compatible API with unified conversion methods.
|
|
392
|
+
*/
|
|
393
|
+
async *_streamingResponseInternal(options) {
|
|
394
|
+
const antConfig = this.transformUniConfigToModelConfig(options.config);
|
|
395
|
+
const antMessages = this.transformUniMessageToModelInput(options.messages, options.signal);
|
|
396
|
+
const stream = (await this._client.beta.messages.create({
|
|
397
|
+
...antConfig,
|
|
398
|
+
messages: antMessages,
|
|
399
|
+
}, {
|
|
400
|
+
signal: options.signal,
|
|
401
|
+
}));
|
|
402
|
+
for await (const event of stream) {
|
|
403
|
+
yield this.transformModelOutputToUniEvent(event);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* List the model ids the configured endpoint serves.
|
|
408
|
+
*
|
|
409
|
+
* @returns The model ids, in the order the endpoint returned them.
|
|
410
|
+
*/
|
|
411
|
+
async listModels() {
|
|
412
|
+
const models = [];
|
|
413
|
+
for await (const model of this._client.models.list()) {
|
|
414
|
+
models.push(model.id);
|
|
415
|
+
}
|
|
416
|
+
return models;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
exports.AntMessagesClient = AntMessagesClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ant_messages/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2025 Prism Shadow. and/or its affiliates
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.AntMessagesClient = void 0;
|
|
17
|
+
var client_1 = require("./client");
|
|
18
|
+
Object.defineProperty(exports, "AntMessagesClient", { enumerable: true, get: function () { return client_1.AntMessagesClient; } });
|