Skip to content

Commit 53d6134

Browse files
committed
refactor: share SDK client context
1 parent 2f829ae commit 53d6134

3 files changed

Lines changed: 103 additions & 15 deletions

File tree

src/sdk/client.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { loadConfig } from "../config/loader";
2-
import { Config } from "../config/schema";
3-
import { request as requestClient, requestJson as requestJsonClient, RequestOpts } from "../client/http";
2+
import type { Config } from "../config/schema";
3+
import { request as requestClient, requestJson as requestJsonClient } from "../client/http";
4+
import type { RequestOpts } from "../client/http";
45
import { parseSSE } from "../client/stream";
56
import { SDKError } from "../errors/base";
67
import { ExitCode } from "../errors/codes";
7-
import { MiniMaxSDKOptions } from "./types";
8+
import type { MiniMaxSDKOptions } from "./types";
89

9-
export class Client {
10-
protected config: Config;
10+
export class ClientContext {
11+
readonly config: Config;
1112

1213
constructor(options: MiniMaxSDKOptions) {
1314
const { apiKey, region, baseUrl } = options;
@@ -25,6 +26,18 @@ export class Client {
2526
async: false,
2627
});
2728
}
29+
}
30+
31+
export class Client {
32+
protected readonly context: ClientContext;
33+
protected readonly config: Config;
34+
35+
constructor(optionsOrContext: MiniMaxSDKOptions | ClientContext) {
36+
this.context = optionsOrContext instanceof ClientContext
37+
? optionsOrContext
38+
: new ClientContext(optionsOrContext);
39+
this.config = this.context.config;
40+
}
2841

2942
protected request(opts: RequestOpts) {
3043
return requestClient(this.config, opts);

src/sdk/index.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { VisionSDK } from "./vision";
88
import { QuotaSDK } from "./quota";
99
import { FileSDK } from "./file";
1010
import { Client } from "./client";
11-
import { MiniMaxSDKOptions } from "./types";
11+
import type { MiniMaxSDKOptions } from "./types";
1212

1313
export class MiniMaxSDK extends Client {
1414
readonly text: TextSDK;
@@ -23,14 +23,14 @@ export class MiniMaxSDK extends Client {
2323

2424
constructor(options: MiniMaxSDKOptions) {
2525
super(options);
26-
this.text = new TextSDK(options);
27-
this.speech = new SpeechSDK(options);
28-
this.image = new ImageSDK(options);
29-
this.video = new VideoSDK(options);
30-
this.music = new MusicSDK(options);
31-
this.search = new SearchSDK(options);
32-
this.vision = new VisionSDK(options);
33-
this.quota = new QuotaSDK(options);
34-
this.file = new FileSDK(options);
26+
this.text = new TextSDK(this.context);
27+
this.speech = new SpeechSDK(this.context);
28+
this.image = new ImageSDK(this.context);
29+
this.video = new VideoSDK(this.context);
30+
this.music = new MusicSDK(this.context);
31+
this.search = new SearchSDK(this.context);
32+
this.vision = new VisionSDK(this.context);
33+
this.quota = new QuotaSDK(this.context);
34+
this.file = new FileSDK(this.context);
3535
}
3636
}

test/sdk/client.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { afterEach, describe, expect, it } from 'bun:test';
2+
import type { Config } from '../../src/config/schema';
3+
import { MiniMaxSDK } from '../../src/sdk';
4+
import { TextSDK } from '../../src/sdk/text';
5+
import { createMockServer, jsonResponse, type MockServer } from '../helpers/mock-server';
6+
7+
interface ClientState {
8+
context: object;
9+
config: Config;
10+
}
11+
12+
function clientState(client: object): ClientState {
13+
return client as ClientState;
14+
}
15+
16+
describe('SDK client context', () => {
17+
let server: MockServer;
18+
19+
afterEach(() => {
20+
server?.close();
21+
});
22+
23+
it('shares one resolved context and config across all child SDKs', () => {
24+
const sdk = new MiniMaxSDK({
25+
apiKey: 'test-key',
26+
baseUrl: 'https://example.com',
27+
});
28+
const rootState = clientState(sdk);
29+
const childClients = [
30+
sdk.text,
31+
sdk.speech,
32+
sdk.image,
33+
sdk.video,
34+
sdk.music,
35+
sdk.search,
36+
sdk.vision,
37+
sdk.quota,
38+
sdk.file,
39+
];
40+
41+
for (const child of childClients) {
42+
expect(clientState(child).context).toBe(rootState.context);
43+
expect(clientState(child).config).toBe(rootState.config);
44+
}
45+
46+
expect(rootState.config.apiKey).toBe('test-key');
47+
expect(rootState.config.baseUrl).toBe('https://example.com');
48+
});
49+
50+
it('preserves direct child SDK construction and request behavior', async () => {
51+
server = createMockServer({
52+
routes: {
53+
'/anthropic/v1/messages': () => jsonResponse({
54+
id: 'msg-direct',
55+
type: 'message',
56+
role: 'assistant',
57+
content: [{ type: 'text', text: 'Hello!' }],
58+
model: 'MiniMax-M3',
59+
stop_reason: 'end_turn',
60+
usage: { input_tokens: 1, output_tokens: 1 },
61+
}),
62+
},
63+
});
64+
65+
const sdk = new TextSDK({
66+
apiKey: 'test-key',
67+
baseUrl: server.url,
68+
});
69+
const result = await sdk.chat({
70+
messages: [{ role: 'user', content: 'Hello' }],
71+
});
72+
73+
expect(result.id).toBe('msg-direct');
74+
});
75+
});

0 commit comments

Comments
 (0)