@erdoai/server 0.1.6 → 0.1.7
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 +91 -2
- package/dist/index.d.ts +119 -3
- package/dist/index.js +3 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ import { ErdoClient } from '@erdoai/server';
|
|
|
17
17
|
|
|
18
18
|
const client = new ErdoClient({
|
|
19
19
|
endpoint: 'https://api.erdo.ai',
|
|
20
|
-
authToken: process.env.
|
|
20
|
+
authToken: process.env.ERDO_AUTH_TOKEN,
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
// Get final result
|
|
@@ -48,6 +48,63 @@ for await (const event of client.invokeStream('data-analyst', {
|
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
### Token Authentication (B2B)
|
|
52
|
+
|
|
53
|
+
For B2B applications, create scoped tokens for your users:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Server-side: Create a scoped token
|
|
57
|
+
const serverClient = new ErdoClient({
|
|
58
|
+
authToken: process.env.ERDO_AUTH_TOKEN,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const { token, expiresAt } = await serverClient.createToken({
|
|
62
|
+
botKeys: ['my-org.data-analyst'],
|
|
63
|
+
externalUserId: 'user_123',
|
|
64
|
+
expiresInSeconds: 3600,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Client-side: Use the scoped token
|
|
68
|
+
const clientClient = new ErdoClient({
|
|
69
|
+
endpoint: 'https://api.erdo.ai',
|
|
70
|
+
token: token, // Scoped token instead of authToken
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const result = await clientClient.invoke('my-org.data-analyst', {
|
|
74
|
+
messages: [{ role: 'user', content: 'Analyze my data' }],
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Thread Management
|
|
79
|
+
|
|
80
|
+
Threads provide persistent conversations. Requires token authentication with `externalUserId`:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const client = new ErdoClient({
|
|
84
|
+
endpoint: 'https://api.erdo.ai',
|
|
85
|
+
token: scopedToken,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Create a thread
|
|
89
|
+
const thread = await client.createThread({ name: 'Data Analysis' });
|
|
90
|
+
|
|
91
|
+
// List user's threads
|
|
92
|
+
const { threads } = await client.listThreads();
|
|
93
|
+
|
|
94
|
+
// Send a message and stream the response
|
|
95
|
+
for await (const event of client.sendMessage(thread.id, {
|
|
96
|
+
content: 'What insights can you find?',
|
|
97
|
+
botKey: 'my-org.data-analyst',
|
|
98
|
+
})) {
|
|
99
|
+
console.log(event.type, event.payload);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Or wait for the complete response
|
|
103
|
+
const events = await client.sendMessageAndWait(thread.id, {
|
|
104
|
+
content: 'Summarize recent trends',
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
51
108
|
### Environment Variables
|
|
52
109
|
|
|
53
110
|
The client can be configured via environment variables:
|
|
@@ -70,11 +127,34 @@ const client = new ErdoClient();
|
|
|
70
127
|
class ErdoClient {
|
|
71
128
|
constructor(options?: {
|
|
72
129
|
endpoint?: string; // API endpoint (default: env or https://api.erdo.ai)
|
|
73
|
-
authToken?: string; // API key (default: env ERDO_AUTH_TOKEN)
|
|
130
|
+
authToken?: string; // API key for server-side (default: env ERDO_AUTH_TOKEN)
|
|
131
|
+
token?: string; // Scoped token for client-side
|
|
74
132
|
});
|
|
75
133
|
|
|
134
|
+
// Token creation (requires authToken)
|
|
135
|
+
createToken(params: CreateTokenParams): Promise<TokenResponse>;
|
|
136
|
+
|
|
137
|
+
// Agent invocation
|
|
76
138
|
invoke(botKey: string, params: InvokeParams): Promise<InvokeResult>;
|
|
77
139
|
invokeStream(botKey: string, params: InvokeParams): AsyncGenerator<SSEEvent>;
|
|
140
|
+
|
|
141
|
+
// Thread management (requires token with externalUserId)
|
|
142
|
+
createThread(params?: CreateThreadParams): Promise<Thread>;
|
|
143
|
+
listThreads(): Promise<ListThreadsResponse>;
|
|
144
|
+
getThread(threadId: string): Promise<Thread>;
|
|
145
|
+
sendMessage(threadId: string, params: SendMessageParams): AsyncGenerator<SSEEvent>;
|
|
146
|
+
sendMessageAndWait(threadId: string, params: SendMessageParams): Promise<SSEEvent[]>;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### `CreateTokenParams`
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
interface CreateTokenParams {
|
|
154
|
+
botKeys?: string[]; // Bot keys the token can access
|
|
155
|
+
datasetIds?: string[]; // Dataset IDs the token can access
|
|
156
|
+
externalUserId?: string; // External user ID for RBAC (enables thread access)
|
|
157
|
+
expiresInSeconds?: number; // Token lifetime (default: 3600)
|
|
78
158
|
}
|
|
79
159
|
```
|
|
80
160
|
|
|
@@ -89,6 +169,15 @@ interface InvokeParams {
|
|
|
89
169
|
}
|
|
90
170
|
```
|
|
91
171
|
|
|
172
|
+
### `SendMessageParams`
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface SendMessageParams {
|
|
176
|
+
content: string; // Message content
|
|
177
|
+
botKey?: string; // Optional bot to use
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
92
181
|
### `SSEEvent`
|
|
93
182
|
|
|
94
183
|
```typescript
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,110 @@
|
|
|
1
|
-
import { ErdoClientConfig,
|
|
1
|
+
import { ErdoClientConfig, CreateTokenParams, TokenResponse, CreateThreadParams, Thread, ListThreadsResponse, SendMessageParams, SSEEvent, InvokeParams, InvokeResult } from '@erdoai/types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Erdo Client for invoking agents
|
|
5
5
|
*
|
|
6
|
+
* Supports two authentication modes:
|
|
7
|
+
* 1. authToken (API key) - for server-side requests, can create tokens
|
|
8
|
+
* 2. token (scoped token) - for client-side requests, limited to token scope
|
|
9
|
+
*
|
|
6
10
|
* Ported from: erdo-python-sdk/erdo/invoke/client.py and invoke.py
|
|
7
11
|
*/
|
|
8
12
|
|
|
9
13
|
declare class ErdoClient {
|
|
10
14
|
private readonly endpoint;
|
|
11
|
-
private readonly authToken
|
|
15
|
+
private readonly authToken?;
|
|
16
|
+
private readonly token?;
|
|
12
17
|
constructor(config?: Partial<ErdoClientConfig>);
|
|
18
|
+
/**
|
|
19
|
+
* Create a scoped token for client-side use
|
|
20
|
+
*
|
|
21
|
+
* Requires authToken (API key) authentication.
|
|
22
|
+
* The returned token can be passed to a client-side ErdoClient for
|
|
23
|
+
* scoped access to specific bots.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* // Server-side: Create a token for the frontend
|
|
28
|
+
* const serverClient = new ErdoClient({ authToken: process.env.ERDO_AUTH_TOKEN });
|
|
29
|
+
* const { token, expiresAt } = await serverClient.createToken({
|
|
30
|
+
* botKeys: ['my-org.data-analyst'],
|
|
31
|
+
* expiresInSeconds: 3600,
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Client-side: Use the token
|
|
35
|
+
* const clientClient = new ErdoClient({ endpoint: 'https://api.erdo.ai', token });
|
|
36
|
+
* const result = await clientClient.invoke('my-org.data-analyst', { messages: [...] });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
createToken(params: CreateTokenParams): Promise<TokenResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* Create a new thread for the external user
|
|
42
|
+
*
|
|
43
|
+
* Requires token authentication with external_user_id.
|
|
44
|
+
* The thread is automatically associated with the external user.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const thread = await client.createThread({ name: 'Support Chat' });
|
|
49
|
+
* console.log(thread.id, thread.name);
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
createThread(params?: CreateThreadParams): Promise<Thread>;
|
|
53
|
+
/**
|
|
54
|
+
* List threads for the external user
|
|
55
|
+
*
|
|
56
|
+
* Requires token authentication with external_user_id.
|
|
57
|
+
* Returns threads that the external user has access to via RBAC.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const { threads } = await client.listThreads();
|
|
62
|
+
* for (const thread of threads) {
|
|
63
|
+
* console.log(thread.id, thread.name);
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
listThreads(): Promise<ListThreadsResponse>;
|
|
68
|
+
/**
|
|
69
|
+
* Get a specific thread by ID
|
|
70
|
+
*
|
|
71
|
+
* Requires token authentication.
|
|
72
|
+
* Access is granted via RBAC external_user or explicit thread scope.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const thread = await client.getThread(threadId);
|
|
77
|
+
* console.log(thread.name, thread.createdAt);
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
getThread(threadId: string): Promise<Thread>;
|
|
81
|
+
/**
|
|
82
|
+
* Send a message to a thread and stream the bot response
|
|
83
|
+
*
|
|
84
|
+
* Requires token authentication.
|
|
85
|
+
* Access is granted via RBAC external_user or explicit thread scope.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* for await (const event of client.sendMessage(threadId, { content: 'Hello!' })) {
|
|
90
|
+
* console.log(event.type, event.payload);
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
sendMessage(threadId: string, params: SendMessageParams): AsyncGenerator<SSEEvent, void, unknown>;
|
|
95
|
+
/**
|
|
96
|
+
* Send a message to a thread and wait for the complete response
|
|
97
|
+
*
|
|
98
|
+
* Requires token authentication.
|
|
99
|
+
* Returns all SSE events collected from the stream.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const events = await client.sendMessageAndWait(threadId, { content: 'Hello!' });
|
|
104
|
+
* console.log('Received', events.length, 'events');
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
sendMessageAndWait(threadId: string, params: SendMessageParams): Promise<SSEEvent[]>;
|
|
13
108
|
/**
|
|
14
109
|
* Invoke an agent and wait for the complete result
|
|
15
110
|
*/
|
|
@@ -67,14 +162,28 @@ declare function invokeStream(botKey: string, params?: InvokeParams): AsyncGener
|
|
|
67
162
|
* Ported from: erdo-python-sdk/erdo/config/config.py
|
|
68
163
|
*/
|
|
69
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Resolved configuration - authToken is optional when using token
|
|
167
|
+
*/
|
|
168
|
+
interface ResolvedConfig {
|
|
169
|
+
endpoint: string;
|
|
170
|
+
authToken?: string;
|
|
171
|
+
token?: string;
|
|
172
|
+
}
|
|
70
173
|
/**
|
|
71
174
|
* Get configuration from environment variables
|
|
72
175
|
*/
|
|
73
176
|
declare function getConfigFromEnv(): ErdoClientConfig;
|
|
74
177
|
/**
|
|
75
178
|
* Merge user config with environment config
|
|
179
|
+
*
|
|
180
|
+
* Auth can be provided via:
|
|
181
|
+
* - authToken: API key for server-side use (can create tokens, full access)
|
|
182
|
+
* - token: Scoped token for client-side use (limited to token scope)
|
|
183
|
+
*
|
|
184
|
+
* At least one of authToken or token must be provided.
|
|
76
185
|
*/
|
|
77
|
-
declare function resolveConfig(userConfig?: Partial<ErdoClientConfig>):
|
|
186
|
+
declare function resolveConfig(userConfig?: Partial<ErdoClientConfig>): ResolvedConfig;
|
|
78
187
|
|
|
79
188
|
/**
|
|
80
189
|
* SSE Client for Server-Sent Events streaming
|
|
@@ -84,6 +193,13 @@ declare function resolveConfig(userConfig?: Partial<ErdoClientConfig>): Required
|
|
|
84
193
|
|
|
85
194
|
/**
|
|
86
195
|
* Parse SSE events from a ReadableStream
|
|
196
|
+
*
|
|
197
|
+
* SSE format:
|
|
198
|
+
* event: <event-type>
|
|
199
|
+
* data: <json-data>
|
|
200
|
+
* <blank line>
|
|
201
|
+
*
|
|
202
|
+
* The event type is extracted from the "event:" line and added to the parsed data.
|
|
87
203
|
*/
|
|
88
204
|
declare function parseSSEStream(response: Response): AsyncGenerator<SSEEvent, void, unknown>;
|
|
89
205
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`);
|
|
1
|
+
var v="https://api.erdo.ai";function E(){let i=typeof process<"u"?process.env:{};return {endpoint:i.ERDO_ENDPOINT||v,authToken:i.ERDO_AUTH_TOKEN}}function k(i){let e=E(),t=i?.endpoint||e.endpoint||v,o=i?.authToken||e.authToken,n=i?.token;return {endpoint:t,authToken:o,token:n}}async function*f(i){if(!i.body)throw new Error("Response body is null");let e=i.body.getReader(),t=new TextDecoder,o="",n,s;try{for(;;){let{done:a,value:u}=await e.read();if(a){if(s!==void 0){let r=w(n,s);r&&(yield r);}break}o+=t.decode(u,{stream:!0});let h=o.split(`
|
|
2
|
+
`);o=h.pop()||"";for(let r of h){let c=r.trim();if(!c){if(s!==void 0){let d=w(n,s);d&&(yield d),n=void 0,s=void 0;}continue}if(c.startsWith("event:"))n=c.slice(6).trim();else if(c.startsWith("data:")){let d=c.slice(5).trim();s===void 0?s=d:s+=`
|
|
3
|
+
`+d;}}}}finally{e.releaseLock();}}function w(i,e){if(!e)return null;try{let t=JSON.parse(e);return i&&(t.type=i),t}catch{return {type:i,raw:e}}}async function p(i){let e=[];for await(let t of f(i))e.push(t);return e}var l=class{endpoint;authToken;token;constructor(e){let t=k(e);if(this.endpoint=t.endpoint,this.authToken=t.authToken,this.token=t.token,!this.authToken&&!this.token)throw new Error("Either authToken or token is required")}async createToken(e){if(!this.authToken)throw new Error("createToken requires authToken (API key) authentication");let t=`${this.endpoint}/tokens`,o={};e.botKeys&&(o.bot_keys=e.botKeys),e.datasetIds&&(o.dataset_ids=e.datasetIds),e.externalUserId&&(o.external_user_id=e.externalUserId),e.expiresInSeconds&&(o.expires_in_seconds=e.expiresInSeconds);let n=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let a=await n.text();throw new Error(`Failed to create token: ${n.status}: ${a}`)}let s=await n.json();return {token:s.token,expiresAt:s.expires_at}}async createThread(e={}){if(!this.token)throw new Error("createThread requires token authentication");let t=`${this.endpoint}/threads`,o={};e.name&&(o.name=e.name),e.datasetIds&&(o.dataset_ids=e.datasetIds);let n=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let a=await n.text();throw new Error(`Failed to create thread: ${n.status}: ${a}`)}let s=await n.json();return {id:s.id,name:s.name,createdAt:s.created_at,updatedAt:s.updated_at}}async listThreads(){if(!this.token)throw new Error("listThreads requires token authentication");let e=`${this.endpoint}/threads-user`,t=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!t.ok){let n=await t.text();throw new Error(`Failed to list threads: ${t.status}: ${n}`)}return {threads:(await t.json()).threads.map(n=>({id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}))}}async getThread(e){if(!this.token)throw new Error("getThread requires token authentication");let t=`${this.endpoint}/threads/${e}`,o=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!o.ok){let s=await o.text();throw new Error(`Failed to get thread: ${o.status}: ${s}`)}let n=await o.json();return {id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}}async*sendMessage(e,t){if(!this.token)throw new Error("sendMessage requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let a=await s.text();throw new Error(`Failed to send message: ${s.status}: ${a}`)}yield*f(s);}async sendMessageAndWait(e,t){if(!this.token)throw new Error("sendMessageAndWait requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let a=await s.text();throw new Error(`Failed to send message: ${s.status}: ${a}`)}return p(s)}async invoke(e,t={}){let o=await this.collectEvents(e,t);return this.extractResult(e,o)}async*invokeStream(e,t={}){let o=await this.makeRequest(e,t);yield*f(o);}async collectEvents(e,t){let o=await this.makeRequest(e,t);return p(o)}async makeRequest(e,t){let o=`${this.endpoint}/bots/${e}/invoke`,n={};t.messages&&(n.messages=t.messages),t.parameters&&(n.parameters=t.parameters),t.datasets&&(n.dataset_slugs=t.datasets),t.mode&&(n.mode=t.mode),t.manualMocks&&(n.manual_mocks=t.manualMocks);let s=this.token||this.authToken,a=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!a.ok){let u=await a.text();throw new Error(`API request failed with status ${a.status}: ${u}`)}return a}extractResult(e,t){let o=[],n=[],s=new Set,a,u;for(let h of t){if(!h)continue;let r=h.payload,c=h.metadata;if(r&&typeof r=="object"&&"invocation_id"in r&&!u&&(u=r.invocation_id),r&&typeof r=="object"&&"action_type"in r&&"key"in r){let d=r.key;s.has(d)||(s.add(d),n.push({key:d,action:r.action_type,status:"completed"}));}if(r&&typeof r=="object"&&"output"in r&&c?.user_visibility==="visible"){let d=r.output;if(d&&typeof d=="object"&&"content"in d){let y=d.content;if(Array.isArray(y)){for(let m of y)if(m.content_type==="text"){let T=c?.role||"assistant";o.push({role:T,content:m.content||""});}}}}r&&typeof r=="object"&&"status"in r&&"output"in r&&(a={status:r.status,parameters:r.parameters,output:r.output,message:r.message,error:r.error});}return {success:true,botKey:e,invocationId:u,result:a,messages:o,events:t,steps:n}}},g=null;function S(){return g||(g=new l),g}async function x(i,e={}){return S().invoke(i,e)}async function*P(i,e={}){yield*S().invokeStream(i,e);}export{l as ErdoClient,p as collectSSEEvents,E as getConfigFromEnv,x as invoke,P as invokeStream,f as parseSSEStream,k as resolveConfig};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erdoai/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Erdo server SDK for invoking agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"directory": "packages/server"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@erdoai/types": "^0.1.
|
|
42
|
+
"@erdoai/types": "^0.1.6"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^24.10.1",
|