@lifebyte-systems-sa/wuyong-client 0.1.3
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 +129 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +75 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @lifebyte-systems-sa/wuyong-client
|
|
2
|
+
|
|
3
|
+
TypeScript client for the Wuyong Orchestrator `/chat` API.
|
|
4
|
+
|
|
5
|
+
The SDK is framework-agnostic and works with any frontend that can provide:
|
|
6
|
+
|
|
7
|
+
- an Auth0 access token
|
|
8
|
+
- one or more selected trading logins
|
|
9
|
+
- a Wuyong Orchestrator base URL
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
From this monorepo workspace:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
npm test --workspace @lifebyte-systems-sa/wuyong-client
|
|
18
|
+
npm run build --workspace @lifebyte-systems-sa/wuyong-client
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
In a frontend application, install the public package from the npm registry:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @lifebyte-systems-sa/wuyong-client
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { WuyongClient, WuyongError } from "@lifebyte-systems-sa/wuyong-client";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Basic usage
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const client = new WuyongClient({
|
|
35
|
+
baseUrl: "https://orchestrator-sa.example.com",
|
|
36
|
+
getAccessToken: () => getAccessTokenSilently(),
|
|
37
|
+
getLogins: () => selectedTradingLogins,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const result = await client.chat({
|
|
41
|
+
threadId: "conversation-123",
|
|
42
|
+
message: "我的账户现在风险怎么样?",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.log(result.answer);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The SDK sends:
|
|
49
|
+
|
|
50
|
+
- `POST /chat`
|
|
51
|
+
- `Authorization: Bearer <Auth0 JWT>`
|
|
52
|
+
- `x-wuyong-logins: 100003,100004`
|
|
53
|
+
- `mode: "member"`
|
|
54
|
+
- `capability: "readonly"`
|
|
55
|
+
|
|
56
|
+
## Auth0
|
|
57
|
+
|
|
58
|
+
`getAccessToken` should return an Auth0 JWT that the Orchestrator can validate.
|
|
59
|
+
|
|
60
|
+
The token should include `user_id` or `sub`. If Member Portal tools are needed, it should also include:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"app_metadata": {
|
|
65
|
+
"php_access_token": "<member-portal-token>"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Do not expose or send `php_refresh_token` to Wuyong.
|
|
71
|
+
|
|
72
|
+
## Trading logins
|
|
73
|
+
|
|
74
|
+
`getLogins` returns the selected trading account logins:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
getLogins: () => [100003]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Multiple logins are sent as a comma-separated `x-wuyong-logins` header.
|
|
81
|
+
|
|
82
|
+
If no logins are available, `client.chat()` throws:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
WuyongError { code: "MISSING_LOGINS" }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Error handling
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
try {
|
|
92
|
+
const result = await client.chat({ threadId, message });
|
|
93
|
+
renderAnswer(result.answer);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error instanceof WuyongError) {
|
|
96
|
+
switch (error.code) {
|
|
97
|
+
case "AUTH_REQUIRED":
|
|
98
|
+
loginWithRedirect();
|
|
99
|
+
break;
|
|
100
|
+
case "MISSING_LOGINS":
|
|
101
|
+
showAccountPicker();
|
|
102
|
+
break;
|
|
103
|
+
case "INVALID_REQUEST":
|
|
104
|
+
showValidationError(error.details);
|
|
105
|
+
break;
|
|
106
|
+
case "SERVER_ERROR":
|
|
107
|
+
case "NETWORK_ERROR":
|
|
108
|
+
showRetryMessage();
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Response
|
|
116
|
+
|
|
117
|
+
`client.chat()` returns:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
type ChatResponse = {
|
|
121
|
+
answer: string;
|
|
122
|
+
facts: Record<string, Fact>;
|
|
123
|
+
audit_events: AuditEvent[];
|
|
124
|
+
ui_events: UiEvent[];
|
|
125
|
+
tool_calls: string[];
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use `answer` for the user-visible response. Use `facts`, `tool_calls`, and `audit_events` for traceability/debug views.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type WuyongErrorCode = "AUTH_REQUIRED" | "INVALID_REQUEST" | "SERVER_ERROR" | "MISSING_LOGINS" | "NETWORK_ERROR";
|
|
2
|
+
export interface WuyongClientOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
getAccessToken: () => Promise<string> | string;
|
|
5
|
+
getLogins: () => Promise<number[]> | number[];
|
|
6
|
+
fetch?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
export interface ChatRequest {
|
|
9
|
+
threadId: string;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
12
|
+
export interface Fact {
|
|
13
|
+
key: string;
|
|
14
|
+
value: Record<string, unknown>;
|
|
15
|
+
source_tool: string;
|
|
16
|
+
source_mcp_server: string;
|
|
17
|
+
ttl_seconds: number;
|
|
18
|
+
ts: string;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface AuditEvent {
|
|
22
|
+
agent: string;
|
|
23
|
+
action: string;
|
|
24
|
+
outcome: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
export interface UiEvent {
|
|
28
|
+
type?: string;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
export interface ChatResponse {
|
|
32
|
+
answer: string;
|
|
33
|
+
facts: Record<string, Fact>;
|
|
34
|
+
audit_events: AuditEvent[];
|
|
35
|
+
ui_events: UiEvent[];
|
|
36
|
+
tool_calls: string[];
|
|
37
|
+
}
|
|
38
|
+
export declare class WuyongError extends Error {
|
|
39
|
+
readonly code: WuyongErrorCode;
|
|
40
|
+
readonly status?: number;
|
|
41
|
+
readonly details?: string;
|
|
42
|
+
constructor(code: WuyongErrorCode, message: string, options?: {
|
|
43
|
+
status?: number;
|
|
44
|
+
details?: string;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export declare class WuyongClient {
|
|
48
|
+
private readonly baseUrl;
|
|
49
|
+
private readonly getAccessToken;
|
|
50
|
+
private readonly getLogins;
|
|
51
|
+
private readonly fetchImpl;
|
|
52
|
+
constructor(options: WuyongClientOptions);
|
|
53
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
54
|
+
private errorFromResponse;
|
|
55
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export class WuyongError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
status;
|
|
4
|
+
details;
|
|
5
|
+
constructor(code, message, options = {}) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "WuyongError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.status = options.status;
|
|
10
|
+
this.details = options.details;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class WuyongClient {
|
|
14
|
+
baseUrl;
|
|
15
|
+
getAccessToken;
|
|
16
|
+
getLogins;
|
|
17
|
+
fetchImpl;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
20
|
+
this.getAccessToken = options.getAccessToken;
|
|
21
|
+
this.getLogins = options.getLogins;
|
|
22
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
23
|
+
}
|
|
24
|
+
async chat(request) {
|
|
25
|
+
const [token, logins] = await Promise.all([this.getAccessToken(), this.getLogins()]);
|
|
26
|
+
if (!logins.length) {
|
|
27
|
+
throw new WuyongError("MISSING_LOGINS", "At least one trading login is required.");
|
|
28
|
+
}
|
|
29
|
+
let response;
|
|
30
|
+
try {
|
|
31
|
+
response = await this.fetchImpl(`${this.baseUrl}/chat`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
Authorization: `Bearer ${token}`,
|
|
36
|
+
"x-wuyong-logins": logins.join(","),
|
|
37
|
+
},
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
thread_id: request.threadId,
|
|
40
|
+
mode: "member",
|
|
41
|
+
capability: "readonly",
|
|
42
|
+
message: request.message,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new WuyongError("NETWORK_ERROR", "Unable to reach Wuyong Orchestrator.", {
|
|
48
|
+
details: error instanceof Error ? error.message : String(error),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw await this.errorFromResponse(response);
|
|
53
|
+
}
|
|
54
|
+
return (await response.json());
|
|
55
|
+
}
|
|
56
|
+
async errorFromResponse(response) {
|
|
57
|
+
const details = await response.text();
|
|
58
|
+
if (response.status === 401) {
|
|
59
|
+
return new WuyongError("AUTH_REQUIRED", "Authentication is required.", {
|
|
60
|
+
status: response.status,
|
|
61
|
+
details,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (response.status === 422) {
|
|
65
|
+
return new WuyongError("INVALID_REQUEST", "The Wuyong request is invalid.", {
|
|
66
|
+
status: response.status,
|
|
67
|
+
details,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return new WuyongError("SERVER_ERROR", "Wuyong Orchestrator returned an error.", {
|
|
71
|
+
status: response.status,
|
|
72
|
+
details,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lifebyte-systems-sa/wuyong-client",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/lifebyte-systems-sa/ai-routers.git",
|
|
20
|
+
"directory": "packages/wuyong-client"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"registry": "https://registry.npmjs.org/"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -p tsconfig.json",
|
|
28
|
+
"prepack": "npm run build",
|
|
29
|
+
"test": "vitest run"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vitest": "^4.0.8"
|
|
34
|
+
}
|
|
35
|
+
}
|