@acosmi/sdk-ts 1.4.1 → 1.4.2

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.
@@ -0,0 +1,125 @@
1
+ import { P as ProviderAdapter, k as ProviderFormat, a as ModelCapabilities, C as ChatRequest, b as ChatResponse, S as StreamEvent } from './index-DEA6LXw6.cjs';
2
+
3
+ /**
4
+ * Anthropic 内容块
5
+ * 覆盖: text / thinking / redacted_thinking / tool_use / tool_result /
6
+ * server_tool_use / mcp_tool_use / mcp_tool_result
7
+ */
8
+ interface AnthropicContentBlock {
9
+ type: string;
10
+ text?: string;
11
+ /** tool_use / server_tool_use / mcp_tool_use block ID */
12
+ id?: string;
13
+ /** tool_use function name */
14
+ name?: string;
15
+ /** tool_use arguments (json.RawMessage) */
16
+ input?: unknown;
17
+ /** thinking block content */
18
+ thinking?: string;
19
+ /** text — web_search 搜索引用 */
20
+ citations?: unknown;
21
+ /** thinking — Anthropic 签名 (后续请求必须回传) */
22
+ signature?: string;
23
+ /** redacted_thinking — base64 编码的被审查思考内容 */
24
+ data?: string;
25
+ /** server_tool_use / mcp_tool_use / mcp_tool_result — 服务端工具来源 */
26
+ server_name?: string;
27
+ /** mcp_tool_use — MCP 调用者上下文 */
28
+ caller?: unknown;
29
+ /** tool_result / mcp_tool_result — 工具执行结果 */
30
+ tool_use_id?: string;
31
+ content?: unknown;
32
+ is_error?: boolean;
33
+ }
34
+ /** Anthropic token 用量 */
35
+ interface AnthropicUsage {
36
+ input_tokens: number;
37
+ output_tokens: number;
38
+ cache_creation_input_tokens?: number;
39
+ cache_read_input_tokens?: number;
40
+ }
41
+ /**
42
+ * Anthropic 原生格式同步响应
43
+ * POST /managed-models/:id/anthropic 返回此格式 (无 response.Success 包装)
44
+ */
45
+ interface AnthropicResponse {
46
+ id: string;
47
+ /** "message" */
48
+ type: string;
49
+ /** "assistant" */
50
+ role: string;
51
+ content: AnthropicContentBlock[];
52
+ model: string;
53
+ stop_reason: string;
54
+ stop_sequence?: string | null;
55
+ usage: AnthropicUsage;
56
+ }
57
+ /** 提取所有 text 类型内容块的文本,拼接返回 */
58
+ declare function anthropicResponseTextContent(r: AnthropicResponse): string;
59
+ /** 提取所有 thinking 类型内容块的文本,拼接返回 */
60
+ declare function anthropicResponseThinkingContent(r: AnthropicResponse): string;
61
+ /** 返回所有 tool_use 类型的内容块 */
62
+ declare function anthropicResponseToolUseBlocks(r: AnthropicResponse): AnthropicContentBlock[];
63
+
64
+ /** 实现 ProviderAdapter, 用于所有非 Anthropic 厂商 */
65
+ declare class OpenAIAdapter implements ProviderAdapter {
66
+ format(): ProviderFormat;
67
+ endpointSuffix(): string;
68
+ /**
69
+ * 构建 OpenAI 兼容格式请求体
70
+ * 不注入 Anthropic betas, 扩展字段 (thinking/effort/speed) 以通用 JSON 传递
71
+ */
72
+ buildRequestBody(_caps: ModelCapabilities, req: ChatRequest): Record<string, unknown>;
73
+ /**
74
+ * 解析 OpenAI 格式同步响应为 ChatResponse
75
+ * 兼容 APIResponse 包装 {"code":0,"data":{...}} 和裸 OpenAI JSON 两种格式
76
+ */
77
+ parseResponse(bodyInput: Uint8Array | string): ChatResponse;
78
+ /**
79
+ * 解析 OpenAI SSE 行
80
+ * [DONE] 标记流结束
81
+ */
82
+ parseStreamLine(eventType: string, data: string): {
83
+ event: StreamEvent;
84
+ done: boolean;
85
+ };
86
+ }
87
+ /**
88
+ * 把 Anthropic 心智模型的 thinking/effort 翻译成 OpenAI `reasoning_effort` 字段值
89
+ * 返回空串表示不设置
90
+ */
91
+ declare function resolveOpenAIReasoningEffort(req: ChatRequest): string;
92
+ /**
93
+ * 把 outputConfig 翻译成 OpenAI response_format
94
+ * 返回 null 表示不设置
95
+ */
96
+ declare function resolveOpenAIResponseFormat(req: ChatRequest): Record<string, unknown> | null;
97
+ /**
98
+ * 解析 OpenAI 格式响应并转换为 AnthropicResponse
99
+ * 用于 chatMessagesOpenAI 方法, 使 Hub 层无需感知 provider 差异
100
+ */
101
+ declare function parseOpenAIResponseToAnthropic(raw: string | Uint8Array): AnthropicResponse;
102
+ /**
103
+ * 将 OpenAI SSE chunks 转换为 Anthropic 兼容的 StreamEvent
104
+ * 有状态: 跨 chunk 追踪 block 索引
105
+ */
106
+ declare class OpenAIStreamConverter {
107
+ private messageStarted;
108
+ private thinkingStarted;
109
+ private thinkingStopped;
110
+ private textStarted;
111
+ /** OpenAI tool_call index → Anthropic block index */
112
+ private toolBlockIndex;
113
+ private blockIndex;
114
+ /**
115
+ * 将一行 OpenAI SSE data 转换为零或多个 Anthropic 格式 StreamEvent
116
+ * 返回 { events, done }
117
+ */
118
+ convert(data: string): {
119
+ events: StreamEvent[];
120
+ done: boolean;
121
+ };
122
+ }
123
+ declare function newOpenAIStreamConverter(): OpenAIStreamConverter;
124
+
125
+ export { type AnthropicResponse as A, OpenAIAdapter as O, type AnthropicContentBlock as a, type AnthropicUsage as b, anthropicResponseTextContent as c, anthropicResponseThinkingContent as d, anthropicResponseToolUseBlocks as e, OpenAIStreamConverter as f, resolveOpenAIResponseFormat as g, newOpenAIStreamConverter as n, parseOpenAIResponseToAnthropic as p, resolveOpenAIReasoningEffort as r };
@@ -0,0 +1,125 @@
1
+ import { P as ProviderAdapter, k as ProviderFormat, a as ModelCapabilities, C as ChatRequest, b as ChatResponse, S as StreamEvent } from './index-DEA6LXw6.js';
2
+
3
+ /**
4
+ * Anthropic 内容块
5
+ * 覆盖: text / thinking / redacted_thinking / tool_use / tool_result /
6
+ * server_tool_use / mcp_tool_use / mcp_tool_result
7
+ */
8
+ interface AnthropicContentBlock {
9
+ type: string;
10
+ text?: string;
11
+ /** tool_use / server_tool_use / mcp_tool_use block ID */
12
+ id?: string;
13
+ /** tool_use function name */
14
+ name?: string;
15
+ /** tool_use arguments (json.RawMessage) */
16
+ input?: unknown;
17
+ /** thinking block content */
18
+ thinking?: string;
19
+ /** text — web_search 搜索引用 */
20
+ citations?: unknown;
21
+ /** thinking — Anthropic 签名 (后续请求必须回传) */
22
+ signature?: string;
23
+ /** redacted_thinking — base64 编码的被审查思考内容 */
24
+ data?: string;
25
+ /** server_tool_use / mcp_tool_use / mcp_tool_result — 服务端工具来源 */
26
+ server_name?: string;
27
+ /** mcp_tool_use — MCP 调用者上下文 */
28
+ caller?: unknown;
29
+ /** tool_result / mcp_tool_result — 工具执行结果 */
30
+ tool_use_id?: string;
31
+ content?: unknown;
32
+ is_error?: boolean;
33
+ }
34
+ /** Anthropic token 用量 */
35
+ interface AnthropicUsage {
36
+ input_tokens: number;
37
+ output_tokens: number;
38
+ cache_creation_input_tokens?: number;
39
+ cache_read_input_tokens?: number;
40
+ }
41
+ /**
42
+ * Anthropic 原生格式同步响应
43
+ * POST /managed-models/:id/anthropic 返回此格式 (无 response.Success 包装)
44
+ */
45
+ interface AnthropicResponse {
46
+ id: string;
47
+ /** "message" */
48
+ type: string;
49
+ /** "assistant" */
50
+ role: string;
51
+ content: AnthropicContentBlock[];
52
+ model: string;
53
+ stop_reason: string;
54
+ stop_sequence?: string | null;
55
+ usage: AnthropicUsage;
56
+ }
57
+ /** 提取所有 text 类型内容块的文本,拼接返回 */
58
+ declare function anthropicResponseTextContent(r: AnthropicResponse): string;
59
+ /** 提取所有 thinking 类型内容块的文本,拼接返回 */
60
+ declare function anthropicResponseThinkingContent(r: AnthropicResponse): string;
61
+ /** 返回所有 tool_use 类型的内容块 */
62
+ declare function anthropicResponseToolUseBlocks(r: AnthropicResponse): AnthropicContentBlock[];
63
+
64
+ /** 实现 ProviderAdapter, 用于所有非 Anthropic 厂商 */
65
+ declare class OpenAIAdapter implements ProviderAdapter {
66
+ format(): ProviderFormat;
67
+ endpointSuffix(): string;
68
+ /**
69
+ * 构建 OpenAI 兼容格式请求体
70
+ * 不注入 Anthropic betas, 扩展字段 (thinking/effort/speed) 以通用 JSON 传递
71
+ */
72
+ buildRequestBody(_caps: ModelCapabilities, req: ChatRequest): Record<string, unknown>;
73
+ /**
74
+ * 解析 OpenAI 格式同步响应为 ChatResponse
75
+ * 兼容 APIResponse 包装 {"code":0,"data":{...}} 和裸 OpenAI JSON 两种格式
76
+ */
77
+ parseResponse(bodyInput: Uint8Array | string): ChatResponse;
78
+ /**
79
+ * 解析 OpenAI SSE 行
80
+ * [DONE] 标记流结束
81
+ */
82
+ parseStreamLine(eventType: string, data: string): {
83
+ event: StreamEvent;
84
+ done: boolean;
85
+ };
86
+ }
87
+ /**
88
+ * 把 Anthropic 心智模型的 thinking/effort 翻译成 OpenAI `reasoning_effort` 字段值
89
+ * 返回空串表示不设置
90
+ */
91
+ declare function resolveOpenAIReasoningEffort(req: ChatRequest): string;
92
+ /**
93
+ * 把 outputConfig 翻译成 OpenAI response_format
94
+ * 返回 null 表示不设置
95
+ */
96
+ declare function resolveOpenAIResponseFormat(req: ChatRequest): Record<string, unknown> | null;
97
+ /**
98
+ * 解析 OpenAI 格式响应并转换为 AnthropicResponse
99
+ * 用于 chatMessagesOpenAI 方法, 使 Hub 层无需感知 provider 差异
100
+ */
101
+ declare function parseOpenAIResponseToAnthropic(raw: string | Uint8Array): AnthropicResponse;
102
+ /**
103
+ * 将 OpenAI SSE chunks 转换为 Anthropic 兼容的 StreamEvent
104
+ * 有状态: 跨 chunk 追踪 block 索引
105
+ */
106
+ declare class OpenAIStreamConverter {
107
+ private messageStarted;
108
+ private thinkingStarted;
109
+ private thinkingStopped;
110
+ private textStarted;
111
+ /** OpenAI tool_call index → Anthropic block index */
112
+ private toolBlockIndex;
113
+ private blockIndex;
114
+ /**
115
+ * 将一行 OpenAI SSE data 转换为零或多个 Anthropic 格式 StreamEvent
116
+ * 返回 { events, done }
117
+ */
118
+ convert(data: string): {
119
+ events: StreamEvent[];
120
+ done: boolean;
121
+ };
122
+ }
123
+ declare function newOpenAIStreamConverter(): OpenAIStreamConverter;
124
+
125
+ export { type AnthropicResponse as A, OpenAIAdapter as O, type AnthropicContentBlock as a, type AnthropicUsage as b, anthropicResponseTextContent as c, anthropicResponseThinkingContent as d, anthropicResponseToolUseBlocks as e, OpenAIStreamConverter as f, resolveOpenAIResponseFormat as g, newOpenAIStreamConverter as n, parseOpenAIResponseToAnthropic as p, resolveOpenAIReasoningEffort as r };
@@ -0,0 +1,118 @@
1
+ // examples/agent-runs-stream.ts — Agent Run Gateway 示例(创建 / 流式 / 本地工具 / 产物)。
2
+ //
3
+ // 演示:
4
+ // 1. 创建一个 agent run (client.agentRuns.create)
5
+ // 2. 流式消费 run 事件 (client.agentRuns.stream),断线可 durable replay
6
+ // 3. 显式 opt-in 本地只读工具桥:处理 local_tool_request,回传 submitLocalToolResult
7
+ // 4. 下载产物 (downloadArtifact)
8
+ // 5. 处理 usage / settle 事件,从 UI 取消按钮安全调用 cancel
9
+ //
10
+ // 红线:
11
+ // - 下游产品禁止直连 Nexus 内部 /api/v4/chat/completions 实现智能体循环,必须走
12
+ // client.agentRuns。
13
+ // - SDK 不内置任何 CrabDesign/CrabCode 专属文件读取逻辑;local_tool_request 只定义
14
+ // 协议,handler 由下游显式提供,allowedTools 用稳定 ASCII function name。
15
+ // - 结算只用 provider/ADK 透传的精确 usage;exact !== true 时服务端释放 hold,
16
+ // 不会用估算 token 扣费。
17
+
18
+ import { Client, allScopes, AgentRunStreamError } from '@acosmi/sdk-ts';
19
+
20
+ async function main() {
21
+ const serverURL = process.env.ACOSMI_SERVER_URL;
22
+ if (!serverURL) {
23
+ throw new Error('ACOSMI_SERVER_URL is required');
24
+ }
25
+ const client = await Client.create({ serverURL });
26
+ await client.login('Agent Runs Example', allScopes());
27
+
28
+ // 1) 创建 run。create 是 POST 副作用操作,401 不自动 refresh 重放。
29
+ const run = await client.agentRuns.create({
30
+ appId: 'crabdesign',
31
+ mode: 'design',
32
+ input: 'Create a landing page mockup for a fintech dashboard',
33
+ activeSkillIds: ['brand-system'],
34
+ knowledgeBaseIds: ['kb-product'],
35
+ // 本地只读上下文策略:显式 opt-in,限制可用工具与读取上限。
36
+ localContextPolicy: {
37
+ enabled: true,
38
+ readonly: true,
39
+ maxBytes: 128_000,
40
+ allowedTools: ['read_file'],
41
+ },
42
+ artifactPolicy: { enabled: true, maxFiles: 10 },
43
+ });
44
+ console.log('[run created] runId=', run.runId, 'status=', run.status);
45
+
46
+ // 2) 流式消费。stream 支持 durable replay:断线后重连同一 run 会先回放已持久化事件。
47
+ // throwOnError:false → 自行消费 error 事件而不是直接抛 AgentRunStreamError。
48
+ try {
49
+ for await (const event of client.agentRuns.stream(run.runId, { throwOnError: false })) {
50
+ switch (event.type) {
51
+ case 'text_delta':
52
+ process.stdout.write(event.text);
53
+ break;
54
+ case 'reasoning_delta':
55
+ console.debug('\n[reasoning]', event.text);
56
+ break;
57
+ case 'local_tool_request': {
58
+ // 3) 本地工具桥 — 由下游产品代码拥有,这里只是只读演示实现。
59
+ const result = await handleLocalTool(event.name, event.input);
60
+ await client.agentRuns.submitLocalToolResult(run.runId, {
61
+ requestId: event.requestId,
62
+ ok: result.ok,
63
+ content: result.content,
64
+ error: result.error,
65
+ });
66
+ break;
67
+ }
68
+ case 'artifact': {
69
+ // 4) 下载产物 — GET 安全查询,允许单次 401 refresh 重试。
70
+ const file = await client.agentRuns.downloadArtifact(run.runId, event.artifact.id);
71
+ console.log('\n[artifact]', file.filename, file.contentType,
72
+ file.data.byteLength, 'bytes');
73
+ break;
74
+ }
75
+ case 'usage':
76
+ console.log('\n[usage] totalTokens=', event.usage.totalTokens,
77
+ 'exact=', event.usage.exact);
78
+ break;
79
+ case 'settle':
80
+ console.log('[settle] status=', event.settlement.status,
81
+ 'tokenRemaining=', event.settlement.tokenRemaining);
82
+ break;
83
+ case 'error':
84
+ console.error('\n[error]', event.error.code, event.error.message);
85
+ break;
86
+ case 'done':
87
+ console.log('\n[done] status=', event.status);
88
+ break;
89
+ }
90
+ }
91
+ } catch (e) {
92
+ if (e instanceof AgentRunStreamError) {
93
+ console.error('agent run stream error:', e.code, e.stage, 'retryable=', e.retryable);
94
+ } else {
95
+ throw e;
96
+ }
97
+ }
98
+
99
+ // 5) cancel 可从 UI 取消按钮安全调用(即使 run 已结束也不会抛)。
100
+ // await client.agentRuns.cancel(run.runId);
101
+ }
102
+
103
+ // 本地只读工具实现示例。生产环境只暴露受控的只读操作,拒绝越权路径。
104
+ async function handleLocalTool(
105
+ name: string,
106
+ input: unknown,
107
+ ): Promise<{ ok: boolean; content?: unknown; error?: string }> {
108
+ if (name !== 'read_file') {
109
+ return { ok: false, error: `local tool rejected: unsupported tool ${name}` };
110
+ }
111
+ // 这里应做路径白名单校验后读取文件;示例仅返回占位内容。
112
+ return { ok: true, content: { note: 'read-only local context placeholder', input } };
113
+ }
114
+
115
+ main().catch((err) => {
116
+ console.error('agent runs example failed:', err);
117
+ process.exit(1);
118
+ });
@@ -0,0 +1,97 @@
1
+ // examples/auth-oauth-flow.ts — 手动 OAuth 2.1 PKCE 流程示例(CLI / 自定义流程)。
2
+ //
3
+ // 演示:
4
+ // 1. discover — 拉取 OAuth Authorization Server 元数据 (RFC 8414)
5
+ // 2. register — RFC 7591 动态客户端注册
6
+ // 3. authorize — 本地 loopback PKCE 授权 (Node only),拿 authorization code
7
+ // 4. exchangeCode — 用 code + code_verifier 换 token
8
+ // 5. newTokenSet + TokenStore — 持久化 token,后续复用
9
+ // 6. refreshToken — 用 refresh_token 续期
10
+ //
11
+ // 说明:
12
+ // - 大多数场景直接用 `client.login(appName, scopes)` 即可(内部封装了下面全部步骤)。
13
+ // 本示例演示底层 helper,适用于需要自定义授权流程 / 自管 token 的 CLI。
14
+ // - authorize 仅在 Node 环境可用(需要本地 HTTP 回调 server);浏览器侧应自行实现
15
+ // popup window + redirect handler。
16
+
17
+ import {
18
+ discover,
19
+ register,
20
+ authorize,
21
+ exchangeCode,
22
+ refreshToken,
23
+ newTokenSet,
24
+ tokenSetIsExpired,
25
+ FileTokenStore,
26
+ allScopes,
27
+ } from '@acosmi/sdk-ts';
28
+
29
+ async function main() {
30
+ const serverURL = process.env.ACOSMI_SERVER_URL;
31
+ if (!serverURL) {
32
+ throw new Error('ACOSMI_SERVER_URL is required');
33
+ }
34
+ const scopes = allScopes();
35
+ const store = new FileTokenStore(process.env.ACOSMI_TOKEN_FILE ?? './auth-tokens.json');
36
+
37
+ // 0) 已有持久化 token 且未过期 → 直接复用,跳过整个授权流程。
38
+ const existing = await store.load();
39
+ if (existing && !tokenSetIsExpired(existing)) {
40
+ console.log('[token] reusing valid token from store, scope=', existing.scope);
41
+ return;
42
+ }
43
+
44
+ // 1) discover — OAuth Authorization Server 元数据
45
+ const meta = await discover(serverURL);
46
+ console.log('[discover] issuer=', meta.issuer);
47
+ console.log('[discover] token_endpoint=', meta.token_endpoint);
48
+
49
+ // 2) register — 动态注册一个 client,拿到 client_id
50
+ const reg = await register(meta, 'Auth Flow Example');
51
+ console.log('[register] client_id=', reg.client_id);
52
+
53
+ let tokenSet;
54
+
55
+ // 3) refresh-first:store 里有过期 token 但带 refresh_token → 先尝试静默刷新。
56
+ if (existing && existing.refresh_token) {
57
+ try {
58
+ const refreshed = await refreshToken(meta, existing.client_id, existing.refresh_token);
59
+ tokenSet = newTokenSet(refreshed, existing.client_id, serverURL);
60
+ console.log('[refresh] token refreshed without re-authorizing');
61
+ } catch (e) {
62
+ console.warn('[refresh] failed, falling back to full authorize:',
63
+ e instanceof Error ? e.message : e);
64
+ }
65
+ }
66
+
67
+ // 4) 没有可刷新的 token → 走完整 PKCE 授权。
68
+ if (!tokenSet) {
69
+ const { result, verifier } = await authorize(meta, reg.client_id, scopes, {
70
+ handler: (ev) => {
71
+ if (ev.type === 'auth_url') console.log('[authorize] open in browser:', ev.url);
72
+ if (ev.type === 'error') console.error('[authorize] error:', ev.err_code, ev.error);
73
+ },
74
+ });
75
+ console.log('[authorize] received authorization code');
76
+
77
+ // 5) exchangeCode — code + code_verifier 换 token
78
+ const tokenResp = await exchangeCode(
79
+ meta,
80
+ reg.client_id,
81
+ result.code,
82
+ result.redirectURI,
83
+ verifier,
84
+ );
85
+ tokenSet = newTokenSet(tokenResp, reg.client_id, serverURL);
86
+ console.log('[exchange] access token acquired, scope=', tokenSet.scope);
87
+ }
88
+
89
+ // 6) 持久化 token,供下次启动复用 / Client 直接读取。
90
+ await store.save(tokenSet);
91
+ console.log('[store] token persisted; expires_at=', tokenSet.expires_at);
92
+ }
93
+
94
+ main().catch((err) => {
95
+ console.error('auth oauth flow example failed:', err);
96
+ process.exit(1);
97
+ });
@@ -0,0 +1,85 @@
1
+ // examples/core-chat.ts — Client 基础用法示例(构造 / 配置 / 模型列举 / chat / 流式)。
2
+ //
3
+ // 演示:
4
+ // 1. 构造 Client (serverURL + 可选 FileTokenStore)
5
+ // 2. OAuth 登录 (按业务最小集合申请 scope)
6
+ // 3. 列举托管模型 + 查看配额摘要
7
+ // 4. 同步 chat 调用
8
+ // 5. 流式 chatStreamWithUsage 并聚合 usage / 结算事件
9
+ //
10
+ // 说明:
11
+ // - SDK 自动按 ManagedModel 的 preferredFormat / supportedFormats 选 Anthropic
12
+ // 或 OpenAI adapter,调用方无需关心。
13
+ // - 金额 / 余额字段是 string(避免 JS number 精度损失),不要做浮点运算。
14
+
15
+ import { Client, allScopes, FileTokenStore } from '@acosmi/sdk-ts';
16
+
17
+ async function main() {
18
+ const serverURL = process.env.ACOSMI_SERVER_URL;
19
+ if (!serverURL) {
20
+ throw new Error('ACOSMI_SERVER_URL is required');
21
+ }
22
+
23
+ // 1) 构造 Client。Client.create 会从 store 异步加载已持久化的 token。
24
+ // Node 上不传 store 时默认 ~/.acosmi/tokens.json;这里显式指定一个路径。
25
+ const client = await Client.create({
26
+ serverURL,
27
+ store: new FileTokenStore(process.env.ACOSMI_TOKEN_FILE ?? './core-tokens.json'),
28
+ });
29
+
30
+ // 2) OAuth 登录 — 已有有效 token 时 login 会直接复用,不重复弹浏览器。
31
+ await client.login('Core Chat Example', allScopes());
32
+
33
+ // 3) 列举托管模型 + 配额摘要
34
+ const models = await client.listModels();
35
+ console.log('[models]', models.length, 'available');
36
+ for (const m of models.slice(0, 5)) {
37
+ console.log(' -', m.modelId, `(provider=${m.provider}, enabled=${m.isEnabled})`);
38
+ }
39
+
40
+ const quota = await client.getQuotaSummary();
41
+ console.log('[quota] freeTotalEtu=', quota.freeTotalEtu,
42
+ 'paidTotalEtu=', quota.paidTotalEtu);
43
+
44
+ // 选一个模型:优先 isDefault,否则取第一个启用的。
45
+ const model =
46
+ models.find((m) => m.isDefault && m.isEnabled) ??
47
+ models.find((m) => m.isEnabled);
48
+ if (!model) {
49
+ throw new Error('no enabled model available — 让管理员在网关启用一个模型');
50
+ }
51
+ console.log('[selected model]', model.modelId);
52
+
53
+ // 4) 同步 chat 调用
54
+ const resp = await client.chat(model.modelId, {
55
+ messages: [{ role: 'user', content: '用一句话介绍 TypeScript。' }],
56
+ max_tokens: 256,
57
+ });
58
+ for (const block of resp.content) {
59
+ if (block.type === 'text' && block.text) {
60
+ console.log('[chat text]', block.text);
61
+ }
62
+ }
63
+ console.log('[chat usage] input=', resp.usage.input_tokens,
64
+ 'output=', resp.usage.output_tokens);
65
+
66
+ // 5) 流式调用 — chatStreamWithUsage 把内容 / sources / 结算事件分流为带标签的迭代项。
67
+ const stream = client.chatStreamWithUsage(model.modelId, {
68
+ messages: [{ role: 'user', content: '写一首关于海的两行短诗。' }],
69
+ max_tokens: 512,
70
+ });
71
+ for await (const item of stream) {
72
+ if (item.kind === 'content' && item.event.event === 'content_block_delta') {
73
+ process.stdout.write('.'); // 实际项目里在此解析 delta 输出 token
74
+ } else if (item.kind === 'settle') {
75
+ console.log('\n[settle] totalTokens=', item.event.totalTokens,
76
+ 'tokenRemaining=', item.event.tokenRemaining);
77
+ }
78
+ }
79
+ console.log('\n[stream] done');
80
+ }
81
+
82
+ main().catch((err) => {
83
+ console.error('core chat example failed:', err);
84
+ process.exit(1);
85
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acosmi/sdk-ts",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Acosmi TypeScript SDK:模型网关、Agent Run Gateway 与 Compliance(电子证据、时间章、报告、签署 envelope)统一客户端,支持浏览器 / Node ≥18 / Deno / Bun。",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.cjs",
@@ -51,7 +51,8 @@
51
51
  "format": "prettier --write \"src/**/*.{ts,json}\"",
52
52
  "typecheck": "tsc --noEmit",
53
53
  "test:pack": "node scripts/smoke-pack.mjs",
54
- "prepublishOnly": "npm run typecheck && npm run lint && npm test && npm run build && npm run test:pack"
54
+ "docs": "typedoc",
55
+ "prepublishOnly": "npm run typecheck && npm run lint && npm test && npm run build && npm run test:pack && npm run docs"
55
56
  },
56
57
  "engines": {
57
58
  "node": ">=18"
@@ -90,6 +91,8 @@
90
91
  "eslint": "^8.57.0",
91
92
  "prettier": "^3.0.0",
92
93
  "tsup": "^8.0.0",
94
+ "typedoc": "^0.28.19",
95
+ "typedoc-plugin-markdown": "^4.11.0",
93
96
  "typescript": "^5.4.0",
94
97
  "vitest": "^1.0.0"
95
98
  }