Skip to content

Commit 58ca653

Browse files
committed
feat(genui-sdk-server): add custom materials support
1 parent 79c7244 commit 58ca653

12 files changed

Lines changed: 91 additions & 19 deletions

File tree

docs/src/components/server/api.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,33 @@ interface IStartServerOptions {
2020
port?: number;
2121
/** 端口被占用时的最大尝试次数,默认 10 */
2222
maxAttempts?: number;
23+
/**
24+
* 启动时固定的物料元数据;传入后用于生成 system prompt。
25+
* 不传则按请求 tinygenui.framework 选择内置物料:
26+
* - Vue → `@opentiny/genui-sdk-materials-vue-opentiny-vue`
27+
* - Angular → `@opentiny/genui-sdk-materials-angular-opentiny-ng`
28+
* - 其他 / 未传 framework → 默认 Vue 物料
29+
*/
30+
materialsMeta?: IMaterialsMeta;
2331
}
2432
```
2533

2634
- **详细信息**
2735

28-
创建一个 Express 应用并启用 CORS,自动注册对话路由(`/chat/completions`)。如果指定端口被占用,会自动尝试下一个端口(最多尝试 `maxAttempts` 次)。启动成功后会在控制台输出服务器地址。
36+
创建一个 Express 应用并启用 CORS,自动注册对话路由(`/chat/completions`)。如果指定端口被占用,会自动尝试下一个端口(最多尝试 `maxAttempts` 次)。启动成功后会在控制台输出服务器地址。物料在启动时通过 `materialsMeta` 固定,请求侧无法切换;未配置时按请求 `framework` 映射内置物料(Vue → OpenTiny Vue,Angular → OpenTiny Angular)。
2937

3038
- **示例**
3139

3240
```typescript
3341
import { startServer } from '@opentiny/genui-sdk-server';
42+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
3443

3544
startServer({
3645
port: 3100,
3746
baseURL: 'https://api.openai.com/v1',
3847
apiKey: '',
3948
maxAttempts: 10,
49+
materialsMeta,
4050
});
4151
```
4252

@@ -59,25 +69,35 @@ interface IEquipChatCompletionsOptions {
5969
apiKey: string;
6070
/** API 基础 URL */
6171
baseURL: string;
72+
/**
73+
* 启动时固定的物料元数据;传入后用于生成 system prompt。
74+
* 不传则按请求 tinygenui.framework 选择内置物料:
75+
* - Vue → `@opentiny/genui-sdk-materials-vue-opentiny-vue`
76+
* - Angular → `@opentiny/genui-sdk-materials-angular-opentiny-ng`
77+
* - 其他 / 未传 framework → 默认 Vue 物料
78+
*/
79+
materialsMeta?: IMaterialsMeta;
6280
}
6381
```
6482

6583
- **详细信息**
6684

67-
创建一个对话请求的实例,创建请求处理器,并将处理器注册到指定的路由路径(POST 方法)。
85+
创建一个对话请求的实例,创建请求处理器,并将处理器注册到指定的路由路径(POST 方法)。物料在装备路由时通过 `materialsMeta` 固定;未配置时按请求 `framework` 映射内置物料(Vue → OpenTiny Vue,Angular → OpenTiny Angular)。
6886

6987
- **示例**
7088

7189
```typescript
7290
import express from 'express';
7391
import { equipChatCompletions } from '@opentiny/genui-sdk-server';
92+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
7493

7594
const app = express();
7695

7796
equipChatCompletions(app, {
7897
route: '/chat/completions',
7998
apiKey: '',
8099
baseURL: 'https://api.openai.com/v1',
100+
materialsMeta,
81101
});
82102

83103
app.listen(3000);

docs/src/en/components/server/api.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,33 @@ interface IStartServerOptions {
2020
port?: number;
2121
/** Max attempts when port is in use, default 10 */
2222
maxAttempts?: number;
23+
/**
24+
* Materials metadata fixed at startup for system prompt generation.
25+
* If omitted, built-in materials are selected by request tinygenui.framework:
26+
* - Vue → `@opentiny/genui-sdk-materials-vue-opentiny-vue`
27+
* - Angular → `@opentiny/genui-sdk-materials-angular-opentiny-ng`
28+
* - Other / missing framework → Vue materials by default
29+
*/
30+
materialsMeta?: IMaterialsMeta;
2331
}
2432
```
2533

2634
- **Details**
2735

28-
Creates an Express app with CORS enabled and registers the chat route (`/chat/completions`). If the specified port is in use, it automatically tries the next port (up to `maxAttempts` times). On success, the server address is printed to the console.
36+
Creates an Express app with CORS enabled and registers the chat route (`/chat/completions`). If the specified port is in use, it automatically tries the next port (up to `maxAttempts` times). On success, the server address is printed to the console. Materials are fixed at startup via `materialsMeta` and cannot be switched per request. When omitted, they follow the request `framework` mapping (Vue → OpenTiny Vue, Angular → OpenTiny Angular).
2937

3038
- **Example**
3139

3240
```typescript
3341
import { startServer } from '@opentiny/genui-sdk-server';
42+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
3443

3544
startServer({
3645
port: 3100,
3746
baseURL: 'https://api.openai.com/v1',
3847
apiKey: '',
3948
maxAttempts: 10,
49+
materialsMeta,
4050
});
4151
```
4252

@@ -59,25 +69,35 @@ interface IEquipChatCompletionsOptions {
5969
apiKey: string;
6070
/** API base URL */
6171
baseURL: string;
72+
/**
73+
* Materials metadata fixed when equipping the route for system prompt generation.
74+
* If omitted, built-in materials are selected by request tinygenui.framework:
75+
* - Vue → `@opentiny/genui-sdk-materials-vue-opentiny-vue`
76+
* - Angular → `@opentiny/genui-sdk-materials-angular-opentiny-ng`
77+
* - Other / missing framework → Vue materials by default
78+
*/
79+
materialsMeta?: IMaterialsMeta;
6280
}
6381
```
6482

6583
- **Details**
6684

67-
Creates a chat completion request instance, builds a request handler, and registers it on the specified route (POST).
85+
Creates a chat completion request instance, builds a request handler, and registers it on the specified route (POST). Materials are fixed via `materialsMeta` when equipping the route. When omitted, they follow the request `framework` mapping (Vue → OpenTiny Vue, Angular → OpenTiny Angular).
6886

6987
- **Example**
7088

7189
```typescript
7290
import express from 'express';
7391
import { equipChatCompletions } from '@opentiny/genui-sdk-server';
92+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
7493

7594
const app = express();
7695

7796
equipChatCompletions(app, {
7897
route: '/chat/completions',
7998
apiKey: '',
8099
baseURL: 'https://api.openai.com/v1',
100+
materialsMeta,
81101
});
82102

83103
app.listen(3000);

docs/src/en/guide/server-usage.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,14 @@ export API_KEY= BASE_URL=https://your-llm-server.com/api && npx genui-sdk-server
7272

7373
```typescript
7474
import { startServer } from '@opentiny/genui-sdk-server';
75+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
7576

7677
startServer({
7778
port: 3100,
7879
baseURL: 'https://api.openai.com/v1',
7980
apiKey: '',
8081
maxAttempts: 10, // Max retries when port is in use
82+
materialsMeta,
8183
});
8284
```
8385

@@ -86,6 +88,7 @@ startServer({
8688
```typescript
8789
import express from 'express';
8890
import { equipChatCompletions } from '@opentiny/genui-sdk-server';
91+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
8992
import cors from 'cors';
9093

9194
const app = express();
@@ -95,6 +98,7 @@ equipChatCompletions(app, {
9598
route: '/chat/completions',
9699
apiKey: '',
97100
baseURL: 'https://api.openai.com/v1',
101+
materialsMeta, // If omitted, default materials are selected by request framework (Vue → OpenTiny Vue, Angular → OpenTiny Angular)
98102
});
99103

100104
app.listen(3000);

docs/src/guide/server-usage.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,14 @@ export API_KEY= BASE_URL=https://your-llm-server.com/api && npx genui-sdk-server
7272

7373
```typescript
7474
import { startServer } from '@opentiny/genui-sdk-server';
75+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
7576

7677
startServer({
7778
port: 3100,
7879
baseURL: 'https://api.openai.com/v1',
7980
apiKey: '',
8081
maxAttempts: 10, // 端口冲突时最大尝试次数
82+
materialsMeta,
8183
});
8284
```
8385

@@ -86,6 +88,7 @@ startServer({
8688
```typescript
8789
import express from 'express';
8890
import { equipChatCompletions } from '@opentiny/genui-sdk-server';
91+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
8992
import cors from 'cors';
9093

9194
const app = express();
@@ -95,6 +98,7 @@ equipChatCompletions(app, {
9598
route: '/chat/completions',
9699
apiKey: '',
97100
baseURL: 'https://api.openai.com/v1',
101+
materialsMeta, // 不传则根据请求的 framework 取默认物料(Vue → OpenTiny Vue,Angular → OpenTiny Angular)
98102
});
99103

100104
app.listen(3000);

packages/chat-completions/src/ai-sdk-chat/ai-sdk-chat-completion.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createOpenAI, type OpenAIProvider } from '@ai-sdk/openai';
33
import { createAnthropic, type AnthropicProvider } from '@ai-sdk/anthropic';
44
import { createDeepSeek, type DeepSeekProvider } from '@ai-sdk/deepseek';
55
import type { ChatCompletionCreateParamsBase } from 'openai/resources/chat/completions';
6-
import type { AsyncIterableStream, ChatCompletionChunk, IRequestOptions } from '../types';
6+
import type { AsyncIterableStream, ChatCompletionChunk, IChatCompletionsConfig, IRequestOptions } from '../types';
77
import { openaiCompatibleTransformChunk } from './openai-compatible-transform';
88
import { ChatCompletions } from '../chat-completions';
99
import { createAsyncIterableStream } from './async-iterable-stream';
@@ -34,8 +34,8 @@ export class AiSdkChatCompletions extends ChatCompletions<AsyncIterableStream<Te
3434
protected readonly providerInstance: ProviderInstance;
3535
protected modelInstance: LanguageModel;
3636

37-
constructor({ apiKey, baseURL, provider }: { apiKey: string; baseURL: string; provider: ProviderType }) {
38-
super();
37+
constructor({ apiKey, baseURL, provider, materialsMeta }: IDefaultModelProviderConfig & { baseURL: string } & IChatCompletionsConfig) {
38+
super({ materialsMeta });
3939
const providerCreator = providerMap[provider];
4040
this.providerInstance = providerCreator({
4141
apiKey,

packages/chat-completions/src/chat-completions.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@ import type {
22
ChatCompletionResponse,
33
ChatCompletionCreateParamsBase,
44
IChatCompletionCreateParams,
5+
IChatCompletionsConfig,
56
IRequestOptions,
67
} from "./types";
78
import { requestTransform } from "./request-transform";
89

910
export abstract class ChatCompletions<T = ChatCompletionResponse, R = ChatCompletionResponse> {
11+
protected readonly materialsMeta?: IChatCompletionsConfig['materialsMeta'];
12+
13+
constructor(config?: IChatCompletionsConfig) {
14+
this.materialsMeta = config?.materialsMeta;
15+
}
16+
1017
protected async preTransform(params: IChatCompletionCreateParams): Promise<ChatCompletionCreateParamsBase> {
11-
return requestTransform(params);
18+
return requestTransform(params, { materialsMeta: this.materialsMeta });
1219
}
1320
protected async postTransform(response: T): Promise<R> {
1421
return response as unknown as R;
@@ -33,4 +40,3 @@ export abstract class ChatCompletions<T = ChatCompletionResponse, R = ChatComple
3340
options?: IRequestOptions
3441
): Promise<T>;
3542
}
36-

packages/chat-completions/src/fetch-chat-completions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ChatCompletions } from "./chat-completions";
22
import type {
33
ChatCompletionCreateParamsBase,
4+
IChatCompletionsConfig,
45
IRequestOptions,
56
ChatCompletionResponse
67
} from "./types";
@@ -9,8 +10,8 @@ export class FetchChatCompletions extends ChatCompletions<Response, ChatCompleti
910
protected readonly apiKey: string;
1011
protected readonly baseURL: string;
1112

12-
constructor(config: { apiKey: string; baseURL: string }) {
13-
super();
13+
constructor(config: { apiKey: string; baseURL: string } & IChatCompletionsConfig) {
14+
super(config);
1415
this.apiKey = config.apiKey;
1516
this.baseURL = config.baseURL.replace(/\/$/, '');
1617
}

packages/chat-completions/src/request-transform.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { genPrompt, type IMaterialsMeta } from '@opentiny/genui-sdk-core';
22
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-opentiny-vue/meta';
33
import { materialsMeta as ngMaterialsMeta } from '@opentiny/genui-sdk-materials-angular-opentiny-ng/meta';
4-
import { IChatCompletionCreateParams, ChatCompletionCreateParamsBase, type IGenPromptConfig } from './types';
4+
import {
5+
IChatCompletionCreateParams,
6+
ChatCompletionCreateParamsBase,
7+
type IChatCompletionsConfig,
8+
type IGenPromptConfig,
9+
} from './types';
510

611

712
type IFrameworkKey = 'Vue' | 'Angular';
@@ -27,7 +32,8 @@ function mergePrompt(
2732
}
2833

2934
export function requestTransform(
30-
params: IChatCompletionCreateParams
35+
params: IChatCompletionCreateParams,
36+
config?: IChatCompletionsConfig,
3137
): ChatCompletionCreateParamsBase {
3238
const newParams = structuredClone(params);
3339

@@ -43,10 +49,10 @@ export function requestTransform(
4349

4450
const { framework = 'Vue', strategy = 'append', ...promptConfig } = tgCustomConfig;
4551

46-
const materialsMetaForFramework =
47-
metaMap[framework] ?? materialsMeta;
52+
const resolvedMaterialsMeta =
53+
config?.materialsMeta ?? metaMap[framework as IFrameworkKey] ?? materialsMeta;
4854
const systemMessages = newParams.messages?.find((message) => message.role === 'system');
49-
const prompt = genPrompt(framework, materialsMetaForFramework, promptConfig);
55+
const prompt = genPrompt(framework, resolvedMaterialsMeta, promptConfig);
5056
if (systemMessages) {
5157
systemMessages.content = mergePrompt(systemMessages.content as string, prompt, strategy);
5258
} else {

packages/chat-completions/src/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import type { ChatCompletionChunk } from "openai/resources/chat/completions";
22
import type { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions";
3-
import type { IGenPromptCustomConfig } from "@opentiny/genui-sdk-core";
3+
import type { IGenPromptCustomConfig, IMaterialsMeta } from "@opentiny/genui-sdk-core";
44

55
export interface IGenPromptConfig extends IGenPromptCustomConfig {
66
framework?: 'Vue' | 'Angular' | string;
77
strategy?: 'append' | 'override' | 'prepend';
88
}
99

10+
export interface IChatCompletionsConfig {
11+
materialsMeta?: IMaterialsMeta;
12+
}
13+
1014
declare global {
1115
export type JsonSerialized<T> = string & {
1216
__json_serialized: T;

packages/server/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ npx genui-sdk-server -e .env.production
3030

3131
```typescript
3232
import { startServer, equipChatCompletions } from '@opentiny/genui-sdk-server';
33+
import { materialsMeta } from '@opentiny/genui-sdk-materials-vue-element-plus/meta';
3334
import express from 'express';
3435

3536
// Option 1: Use startServer (includes default route /chat/completions)
3637
startServer({
3738
port: 3100,
3839
baseURL: 'https://api.openai.com/v1',
3940
apiKey: 'your-api-key',
41+
materialsMeta, // optional: fix materials at startup
4042
});
4143

4244
// Option 2: Custom Express app
@@ -45,6 +47,7 @@ equipChatCompletions(app, {
4547
route: '/api/chat',
4648
baseURL: 'https://api.openai.com/v1',
4749
apiKey: 'your-api-key',
50+
materialsMeta,
4851
});
4952
app.listen(3100);
5053
```

0 commit comments

Comments
 (0)