@h-ai/serv 0.1.0-alpha.34 → 0.1.0-alpha.36
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 +25 -0
- package/dist/features/ai.d.ts +1 -1
- package/dist/features/iam.d.ts +1 -1
- package/dist/features/storage.d.ts +1 -1
- package/dist/index.d.ts +87 -3
- package/dist/index.js +412 -50
- package/dist/index.js.map +1 -1
- package/dist/{serv-context-CwewduFi.d.ts → serv-context-CatIx8r-.d.ts} +1 -1
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
- 默认 feature procedures:`createIamProcedures()`、`createStorageProcedures()`、`createAiProcedures()`
|
|
11
11
|
- 内置安全响应头、健康检查、可选 OpenAPI JSON、可选 Scalar 文档页、可选内部 RPC endpoint
|
|
12
12
|
- 可选传输加密:`serv.createApp({ transport: { crypto } })` 自动挂载密钥协商与请求/响应加解密
|
|
13
|
+
- 可选语音入口:`serv.createApp({ audio: { ai, verifyTicket, authorize } })` 挂载统一语音 WebSocket;一次性 ticket 负责身份验证,授权回调负责服务端确认操作、模型、音色、配额与并发占用
|
|
13
14
|
|
|
14
15
|
> 生命周期说明:`@h-ai/serv` 本身是无状态 HTTP App 装配器,不需要 `init()` / `close()`;请在创建 app 前初始化 `iam` / `storage` / `ai` / `crypto` 等依赖,并在 `serv.listen(..., { onClose })` 中反向释放它们。`serv.createApp()` 遇到启动期配置错误(例如 transport manager 创建失败)会 fail-fast 抛出,便于进程启动阶段暴露问题。
|
|
15
16
|
|
|
@@ -67,6 +68,30 @@ const handler = serv.toFetch(app)
|
|
|
67
68
|
export default { fetch: handler }
|
|
68
69
|
```
|
|
69
70
|
|
|
71
|
+
### Audio WebSocket:ticket 与服务端授权
|
|
72
|
+
|
|
73
|
+
语音入口必须使用短期、一次性 ticket。应用通过已登录 HTTP endpoint 签发 ticket,并在 `verifyTicket` 中原子消费;框架保存返回的 `ServSession`,再调用 `authorize` 确认付费参数:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const app = serv.createApp({
|
|
77
|
+
contract,
|
|
78
|
+
procedures,
|
|
79
|
+
iam,
|
|
80
|
+
audio: {
|
|
81
|
+
ai,
|
|
82
|
+
verifyTicket: consumeAudioTicket,
|
|
83
|
+
authorize: async (session, request) => authorizeAudioRequest(session, request),
|
|
84
|
+
onSessionEnd: async (session, request) => releaseAudioConcurrency(session.userId, request.operation),
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- `verifyTicket(ticket)` 必须校验用途、过期时间并保证同一 ticket 只能成功一次,返回 `HaiResult<ServSession>`。
|
|
90
|
+
- `authorize(session, request)` 适合检查 IAM 权限、模型/音色白名单、Persona、调用配额并占用并发名额;返回的 `AuthorizedAudioRequest` 才会传给 `ai.audio`。
|
|
91
|
+
- 未提供 `authorize` 时,框架会忽略客户端的 `model`、`voice` 与 `instruction`,只使用服务端默认模型。
|
|
92
|
+
- `onSessionEnd` 在成功、失败或断连后至多调用一次,供应用释放并发占用。
|
|
93
|
+
- 浏览器 `createAIClient({ audio: { url, getTicket } })` 只把一次性 `ticket` 放入 WebSocket URL;禁止传普通 IAM access token。
|
|
94
|
+
|
|
70
95
|
### 传输加密(与 `@h-ai/crypto` 统一)
|
|
71
96
|
|
|
72
97
|
serv 不暴露本地传输加密工厂;统一从 `crypto.transport` 创建服务端管理器。使用方只需传入顶层 `crypto` 句柄:
|
package/dist/features/ai.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as zod_v4_core from 'zod/v4/core';
|
|
|
3
3
|
import * as zod from 'zod';
|
|
4
4
|
import * as _orpc_server from '@orpc/server';
|
|
5
5
|
import { AIFunctions } from '@h-ai/ai';
|
|
6
|
-
import {
|
|
6
|
+
import { a as ServContext } from '../serv-context-CatIx8r-.js';
|
|
7
7
|
import '@h-ai/core';
|
|
8
8
|
|
|
9
9
|
/** AI 默认 procedures 依赖。 */
|
package/dist/features/iam.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as zod from 'zod';
|
|
|
4
4
|
import * as _orpc_server from '@orpc/server';
|
|
5
5
|
import { IamFunctions } from '@h-ai/iam';
|
|
6
6
|
export { TokenPair } from '@h-ai/iam';
|
|
7
|
-
import {
|
|
7
|
+
import { a as ServContext } from '../serv-context-CatIx8r-.js';
|
|
8
8
|
import '@h-ai/core';
|
|
9
9
|
|
|
10
10
|
/** IAM 默认 procedures 依赖。 */
|
|
@@ -3,7 +3,7 @@ import * as zod_v4_core from 'zod/v4/core';
|
|
|
3
3
|
import * as zod from 'zod';
|
|
4
4
|
import * as _orpc_server from '@orpc/server';
|
|
5
5
|
import { StorageFunctions } from '@h-ai/storage';
|
|
6
|
-
import {
|
|
6
|
+
import { a as ServContext } from '../serv-context-CatIx8r-.js';
|
|
7
7
|
import '@h-ai/core';
|
|
8
8
|
|
|
9
9
|
/** Storage 默认 procedures 依赖。 */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,45 @@
|
|
|
1
1
|
import * as _h_ai_core from '@h-ai/core';
|
|
2
2
|
import { HaiResult, ValidationFormError } from '@h-ai/core';
|
|
3
3
|
import { Router, implement } from '@orpc/server';
|
|
4
|
+
import { AIFunctions, AudioWsStartMessage, AudioFormat } from '@h-ai/ai';
|
|
4
5
|
import { AnyContractRouter, OpenAPI } from '@orpc/contract';
|
|
6
|
+
import { S as ServSession, a as ServContext, b as ServIam, R as RefreshCookieConfig, C as CreateServContext, p as parseRequestContext, c as buildAuthContextFactory } from './serv-context-CatIx8r-.js';
|
|
7
|
+
export { d as CreateServContextInput, e as RefreshTokenPair } from './serv-context-CatIx8r-.js';
|
|
5
8
|
import { MiddlewareHandler } from 'hono';
|
|
6
|
-
import { S as ServContext, a as ServIam, R as RefreshCookieConfig, b as ServSession, C as CreateServContext, p as parseRequestContext, c as buildAuthContextFactory } from './serv-context-CwewduFi.js';
|
|
7
|
-
export { d as CreateServContextInput, e as RefreshTokenPair } from './serv-context-CwewduFi.js';
|
|
8
9
|
import { z, ZodType } from 'zod';
|
|
9
10
|
import { CryptoFunctions, TransportKeyStore } from '@h-ai/crypto';
|
|
10
11
|
import { ServerType } from '@hono/node-server';
|
|
11
12
|
import { AddressInfo } from 'node:net';
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @h-ai/serv — 统一语音 WebSocket 接入
|
|
16
|
+
*
|
|
17
|
+
* 基于 `@h-ai/ai` 的 `ai.audio` 提供开箱即用的语音 WebSocket 端点,同时支持非实时 / 实时
|
|
18
|
+
* 的语音识别与语音合成。传输细节(WebSocket 帧、二进制/JSON 编解码)在此内部完成,
|
|
19
|
+
* 客户端只使用统一的领域协议,不接触任何厂商原生事件。
|
|
20
|
+
* @module features/serv-feature-audio
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** 语音 WebSocket 接入依赖。 */
|
|
24
|
+
interface AudioWsDeps {
|
|
25
|
+
/** AI 服务对象的语音能力(`ai.audio`)。 */
|
|
26
|
+
readonly ai: Pick<AIFunctions, 'audio'>;
|
|
27
|
+
/** 校验并原子消费短期、一次性的 Audio ticket。 */
|
|
28
|
+
readonly verifyTicket: (ticket: string) => Promise<HaiResult<ServSession>>;
|
|
29
|
+
/** 基于会话授权操作、模型、音色、格式和配额。 */
|
|
30
|
+
readonly authorize?: (session: ServSession, request: AudioWsStartMessage) => Promise<HaiResult<AuthorizedAudioRequest>>;
|
|
31
|
+
/** 会话结束时释放应用侧并发占用。 */
|
|
32
|
+
readonly onSessionEnd?: (session: ServSession, request: AuthorizedAudioRequest) => void | Promise<void>;
|
|
33
|
+
/** 单条消息字节上限(默认 1 MiB,防止内存放大)。 */
|
|
34
|
+
readonly maxMessageBytes?: number;
|
|
35
|
+
/** 单连接累计接收音频字节上限(默认 10 MiB,防止非流式缓冲无限增长)。 */
|
|
36
|
+
readonly maxBufferedBytes?: number;
|
|
37
|
+
/** 单连接累计接收文本字节上限(默认 1 MiB,防止 TTS 文本输入无限增长与上游费用失控)。 */
|
|
38
|
+
readonly maxTextBytes?: number;
|
|
39
|
+
/** 单连接最长持续时间(毫秒,默认 5 分钟,防止恶意长连接)。 */
|
|
40
|
+
readonly maxSessionMs?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
13
43
|
/**
|
|
14
44
|
* @h-ai/serv — Pipeline 共享类型
|
|
15
45
|
*
|
|
@@ -311,6 +341,60 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
|
|
|
311
341
|
* ```
|
|
312
342
|
*/
|
|
313
343
|
readonly transport?: ServTransportConfig;
|
|
344
|
+
/**
|
|
345
|
+
* 启用统一语音 WebSocket 入口(opt-in)。提供后 serv 自动:
|
|
346
|
+
* 1. 在 `${apiPrefix}/ai/audio`(可自定义 `path`)注册语音 WebSocket 路由
|
|
347
|
+
* 2. 基于 `ai.audio` 提供非实时 / 实时的语音识别与合成
|
|
348
|
+
* 3. 通过一次性 Audio ticket 验证身份,再由授权回调确认付费参数
|
|
349
|
+
*
|
|
350
|
+
* 需配合 `serv.listen()`(Node 适配器会自动完成 WebSocket 升级注入)。
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* serv.createApp({ contract, procedures, iam, audio: { ai, verifyTicket, authorize } })
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
readonly audio?: ServAudioConfig;
|
|
358
|
+
}
|
|
359
|
+
/** `serv.createApp()` 的语音 WebSocket 接入配置。 */
|
|
360
|
+
interface ServAudioConfig {
|
|
361
|
+
/** AI 服务对象(提供 `ai.audio` 领域能力)。 */
|
|
362
|
+
readonly ai: AudioWsDeps['ai'];
|
|
363
|
+
/**
|
|
364
|
+
* 校验并原子消费短期、一次性的 Audio ticket。
|
|
365
|
+
*
|
|
366
|
+
* ticket 的签发和存储由应用决定;成功结果必须返回建立授权所需的 IAM 会话摘要。
|
|
367
|
+
*/
|
|
368
|
+
readonly verifyTicket: (ticket: string) => Promise<HaiResult<ServSession>>;
|
|
369
|
+
/**
|
|
370
|
+
* 基于已验证会话授权本次操作,并返回服务端确认后的付费参数。
|
|
371
|
+
*
|
|
372
|
+
* 未提供时只保留操作及音频格式,客户端提交的 model / voice / instruction 不会生效。
|
|
373
|
+
* 这里适合完成 IAM 权限、Persona、套餐配额和并发会话的检查/占用。
|
|
374
|
+
*/
|
|
375
|
+
readonly authorize?: (session: ServSession, request: AudioWsStartMessage) => Promise<HaiResult<AuthorizedAudioRequest>>;
|
|
376
|
+
/** 会话结束钩子,供应用释放并发占用;无论成功、失败或断连都至多调用一次。 */
|
|
377
|
+
readonly onSessionEnd?: (session: ServSession, request: AuthorizedAudioRequest) => void | Promise<void>;
|
|
378
|
+
/** 语音入口路径(相对 apiPrefix,默认 `/ai/audio`)。 */
|
|
379
|
+
readonly path?: string;
|
|
380
|
+
/** 单条消息字节上限(默认 1 MiB)。 */
|
|
381
|
+
readonly maxMessageBytes?: number;
|
|
382
|
+
/** 单连接累计接收音频字节上限(默认 10 MiB)。 */
|
|
383
|
+
readonly maxBufferedBytes?: number;
|
|
384
|
+
/** 单连接累计接收文本字节上限(默认 1 MiB)。 */
|
|
385
|
+
readonly maxTextBytes?: number;
|
|
386
|
+
/** 单连接最长持续时间(毫秒,默认 5 分钟)。 */
|
|
387
|
+
readonly maxSessionMs?: number;
|
|
388
|
+
}
|
|
389
|
+
/** Audio WebSocket 经应用授权后允许生效的服务端参数。 */
|
|
390
|
+
interface AuthorizedAudioRequest {
|
|
391
|
+
readonly operation: 'transcribe' | 'synthesize';
|
|
392
|
+
/** 服务端允许的模型 ID;不传时使用 `ai.audio` 对应操作的默认模型。 */
|
|
393
|
+
readonly model?: string;
|
|
394
|
+
readonly voice?: string;
|
|
395
|
+
readonly instruction?: string;
|
|
396
|
+
readonly format?: AudioFormat;
|
|
397
|
+
readonly sampleRate?: number;
|
|
314
398
|
}
|
|
315
399
|
/**
|
|
316
400
|
* 创建并装配 HTTP API app。
|
|
@@ -684,4 +768,4 @@ declare const serv: {
|
|
|
684
768
|
m: (key: "$schema" | "serv_validationFailed" | "serv_validationRequired" | "serv_validationInvalid" | "serv_validationInvalidType" | "serv_validationStringMin" | "serv_validationStringMax" | "serv_validationNumberMin" | "serv_validationNumberMax" | "serv_validationArrayMin" | "serv_validationArrayMax" | "serv_validationTooSmall" | "serv_validationTooBig" | "serv_validationEmail" | "serv_validationUrl" | "serv_validationUuid" | "serv_validationEnum" | "serv_errorUnauthorized" | "serv_errorForbidden" | "serv_errorInternal" | "serv_storageKeyRequired" | "serv_storageKeyTooLong" | "serv_storageKeyIllegalChars" | "serv_storageKeyDotSegments" | "serv_iamCannotDeleteCurrentUser" | "serv_transportInvalidPayload" | "serv_transportUnknownClient" | "serv_transportClientIdRequired" | "serv_transportClientKeyNotFound" | "serv_transportDecryptFailed" | "serv_transportEncryptFailed" | "serv_transportKeyExchangeFailed", options?: _h_ai_core.MessageOptions) => string;
|
|
685
769
|
};
|
|
686
770
|
|
|
687
|
-
export { type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServDocsHttpConfig, type ServFetchHandler, type ServGuardedProcedureWrapper, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, type ServProcedureWrapper, type ServRpcHttpConfig, ServSession, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };
|
|
771
|
+
export { type AuthorizedAudioRequest, type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServAudioConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServDocsHttpConfig, type ServFetchHandler, type ServGuardedProcedureWrapper, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, type ServProcedureWrapper, type ServRpcHttpConfig, ServSession, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };
|