@tagit/ai-client-core 0.5.0 → 0.6.0
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 +71 -16
- package/dist/client.d.ts +16 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +137 -36
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +77 -21
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +13 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/transport.d.ts +1 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +2 -1
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +66 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
- package/dist/client.test.d.ts +0 -2
- package/dist/client.test.d.ts.map +0 -1
- package/dist/client.test.js +0 -544
- package/dist/client.test.js.map +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -29
- package/dist/config.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -52,6 +52,18 @@ import { AIClient, createDefaultConfig } from '@tagit/ai-client-core';
|
|
|
52
52
|
|
|
53
53
|
const client = new AIClient(
|
|
54
54
|
createDefaultConfig('uc-default-1lji40', 'https://ai.tagit.live', {
|
|
55
|
+
user: {
|
|
56
|
+
entityId: 'anonymous-or-authenticated-user-id',
|
|
57
|
+
userId: 'optional-friendly-user-id',
|
|
58
|
+
entityidType: 'internal_user_id',
|
|
59
|
+
entityidAlgo: 'native',
|
|
60
|
+
entityidOwner: 'your-service',
|
|
61
|
+
},
|
|
62
|
+
conversation: {
|
|
63
|
+
conversationId: 'optional-durable-conversation-id',
|
|
64
|
+
title: 'Optional conversation title',
|
|
65
|
+
memoryMaxTurns: 6,
|
|
66
|
+
},
|
|
55
67
|
orchestrator: {
|
|
56
68
|
apiKey: import.meta.env.VITE_TAGIT_AI_ORCHESTRATOR_API_KEY,
|
|
57
69
|
},
|
|
@@ -68,7 +80,7 @@ const client = new AIClient(
|
|
|
68
80
|
|
|
69
81
|
`AIClient` is the stateful engine:
|
|
70
82
|
|
|
71
|
-
- builds the `/
|
|
83
|
+
- builds the `/v2/chat` request
|
|
72
84
|
- manages the conversation transcript
|
|
73
85
|
- streams assistant output
|
|
74
86
|
- retries transient failures
|
|
@@ -81,7 +93,7 @@ const client = new AIClient(
|
|
|
81
93
|
|
|
82
94
|
### `OrchestratorRequest`
|
|
83
95
|
|
|
84
|
-
`OrchestratorRequest` is the payload posted to the orchestrator `/
|
|
96
|
+
`OrchestratorRequest` is the payload posted to the orchestrator `/v2/chat` endpoint.
|
|
85
97
|
|
|
86
98
|
### `OrchestratorChunk`
|
|
87
99
|
|
|
@@ -106,14 +118,38 @@ const config = createDefaultConfig('uc-default-1lji40', 'https://ai.tagit.live',
|
|
|
106
118
|
| Field | Type / values | Default | What it does | Example |
|
|
107
119
|
|---|---|---:|---|---|
|
|
108
120
|
| `useCaseId` | `string` | required | Runtime use case identifier used by the orchestrator. | `'uc-default-1lji40'` |
|
|
109
|
-
| `orchestratorUrl` | URL string | required | Base URL for the orchestrator. Do not include `/
|
|
121
|
+
| `orchestratorUrl` | URL string | required | Base URL for the orchestrator. Do not include `/v2/chat`. | `'https://ai.tagit.live'` |
|
|
110
122
|
| `options.orchestrator` | partial orchestrator config | `undefined` | Overrides specific orchestrator fields. | `{ apiKey: '...' }` |
|
|
123
|
+
| `options.user` | user identity config | `undefined` | Shared identity used by orchestrator memory and collector events. | `{ entityId: 'user_123' }` |
|
|
124
|
+
| `options.conversation` | conversation config | `undefined` | Durable conversation id, title, and memory window. | `{ memoryMaxTurns: 6 }` |
|
|
111
125
|
| `options.collector` | partial collector config | `undefined` | Enables or customizes collector emission. | `{ enabled: false }` |
|
|
112
126
|
| `options.ui` | partial UI config | `undefined` | Overrides client UI/runtime behavior. | `{ streaming: true }` |
|
|
113
127
|
|
|
128
|
+
### Shared User Config
|
|
129
|
+
|
|
130
|
+
The `user` block is the single identity source for V2. At minimum, production integrations should provide `user.entityId`. When `user.userId` is omitted, the SDK uses `user.entityId` as the orchestrator `user_id` and collector `content.contentData.userId`.
|
|
131
|
+
|
|
132
|
+
| Field | Type / values | Required | What it does | Example |
|
|
133
|
+
|---|---|---:|---|---|
|
|
134
|
+
| `user.entityId` | string | yes for production | Primary user or anonymous entity identifier supplied by the host app. | `visitor_abc123` |
|
|
135
|
+
| `user.userId` | string | no | Optional friendly/application user id. Defaults to `entityId`. | `sandro` |
|
|
136
|
+
| `user.entityidType` | string | no | Entity type metadata for collector normalization. | `internal_user_id` |
|
|
137
|
+
| `user.entityidAlgo` | string | no | Entity ID algorithm metadata. | `native` |
|
|
138
|
+
| `user.entityidOwner` | string | no | Entity owner metadata. | `tagit.live` |
|
|
139
|
+
|
|
140
|
+
### Shared Conversation Config
|
|
141
|
+
|
|
142
|
+
The `conversation` block controls durable memory. The SDK sends only the current user message to the orchestrator; the orchestrator rehydrates recent history server-side using `conversationId` and `userId`.
|
|
143
|
+
|
|
144
|
+
| Field | Type / values | Required | What it does | Example |
|
|
145
|
+
|---|---|---:|---|---|
|
|
146
|
+
| `conversation.conversationId` | string | no | Durable conversation identifier. Generated locally when omitted. | `conv-homepage-001` |
|
|
147
|
+
| `conversation.title` | string | no | Optional user-facing title sent to the orchestrator. | `Homepage assistant` |
|
|
148
|
+
| `conversation.memoryMaxTurns` | nonnegative integer | no | Server-side recent-history window. Use `0` to disable rehydration. | `6` |
|
|
149
|
+
|
|
114
150
|
### Orchestrator Config
|
|
115
151
|
|
|
116
|
-
`orchestrator.baseUrl` should point to the orchestrator root URL, not `/
|
|
152
|
+
`orchestrator.baseUrl` should point to the orchestrator root URL, not `/v2/chat`. The client appends `/v2/chat` internally.
|
|
117
153
|
|
|
118
154
|
| Field | Type / values | Required | What it does | Example |
|
|
119
155
|
|---|---|---:|---|---|
|
|
@@ -134,17 +170,14 @@ Collector is optional and can be disabled. When enabled, the client emits a TagI
|
|
|
134
170
|
| `collector.baseUrl` | URL string | yes | Collector endpoint base URL. | `https://collector.tagit.live` |
|
|
135
171
|
| `collector.tagId` | string | yes | Tag identifier used to route and classify collector events. | `tagit-site-demo` |
|
|
136
172
|
| `collector.acceptCookies` | `boolean` | no | Whether the widget should request/set cookies when emitting collector events. | `true` |
|
|
137
|
-
| `collector.
|
|
138
|
-
| `collector.agentId` | string | no | Optional agent identity for collector payloads. | `uc-default-1lji40` |
|
|
139
|
-
| `collector.entityId` | string | no | Optional entity identifier for the tracked subject. | `acct_456` |
|
|
140
|
-
| `collector.entityidType` | string | no | Entity type metadata. | `account` |
|
|
141
|
-
| `collector.entityidAlgo` | string | no | Entity ID algorithm metadata. | `sha256` |
|
|
142
|
-
| `collector.entityidOwner` | string | no | Entity owner metadata. | `tagit` |
|
|
173
|
+
| `collector.agentId` | string | no | Optional collector agent identity. Defaults to the request use case id. | `uc-default-1lji40` |
|
|
143
174
|
| `collector.eventSubContext` | string | no | Optional sub-context identifier for the event surface. | `website-homepage` |
|
|
144
|
-
| `collector.sessionId` | string | no | Optional session identifier. | `session_abc123` |
|
|
175
|
+
| `collector.sessionId` | string | no | Optional collector session identifier. It is not the primary durable conversation id when `conversation.conversationId` exists. | `session_abc123` |
|
|
145
176
|
| `collector.privacy.jurisdiction` | string | no | Privacy jurisdiction metadata. | `US-CA` |
|
|
146
177
|
| `collector.privacy.purposesConsent.analytics` | `boolean` | no | Consent flag for analytics collection. | `true` |
|
|
147
178
|
|
|
179
|
+
Legacy collector identity fields are still accepted for migration compatibility, but new integrations should put identity in the shared `user` block.
|
|
180
|
+
|
|
148
181
|
### UI Config
|
|
149
182
|
|
|
150
183
|
| Field | Type / values | Default | What it does | Example |
|
|
@@ -177,7 +210,7 @@ const client = new AIClient(config, options);
|
|
|
177
210
|
| `on(listener)` | `(): () => void` | Subscribes to client events and returns an unsubscribe function. | `client.on(handleEvent)` |
|
|
178
211
|
| `sendMessage()` | `(message, options?) => Promise<void>` | Sends a user message and streams the orchestrator response. | `await client.sendMessage('Hello')` |
|
|
179
212
|
| `cancel()` | `(): void` | Cancels the current in-flight request. | `client.cancel()` |
|
|
180
|
-
| `clearHistory()` | `(): void` | Clears the transcript
|
|
213
|
+
| `clearHistory()` | `(): void` | Clears the local transcript. If `conversation.conversationId` is configured, the durable id is preserved. | `client.clearHistory()` |
|
|
181
214
|
| `trimHistoryBefore()` | `(messageId) => string \| null` | Removes a message and everything after it. | `client.trimHistoryBefore(id)` |
|
|
182
215
|
|
|
183
216
|
## Message Options
|
|
@@ -188,11 +221,18 @@ const client = new AIClient(config, options);
|
|
|
188
221
|
|---|---|---|---|
|
|
189
222
|
| `tools` | `'none' \| 'all' \| string[]` | Limits which MCP tools are available for the request. | `tools: 'none'` |
|
|
190
223
|
| `useCaseMessage` | `string` | Injects an additional use-case instruction as a system message. | `useCaseMessage: 'Keep it short and direct.'` |
|
|
224
|
+
| `userId` | `string` | Overrides the resolved user id for this request. | `userId: 'request-user'` |
|
|
225
|
+
| `useCaseId` | `string` | Overrides the configured use case for this request. | `useCaseId: 'uc-other'` |
|
|
226
|
+
| `snapshotId` | `string` | Overrides the configured snapshot for this request. | `snapshotId: 'snap-123'` |
|
|
227
|
+
| `conversationId` | `string` | Overrides the durable conversation id for this request. | `conversationId: 'conv-other'` |
|
|
228
|
+
| `conversationTitle` | `string` | Overrides the conversation title for this request. | `conversationTitle: 'Support chat'` |
|
|
229
|
+
| `conversationMemoryMaxTurns` | `number` | Overrides the server-side memory window for this request. | `conversationMemoryMaxTurns: 0` |
|
|
191
230
|
|
|
192
231
|
```ts
|
|
193
232
|
await client.sendMessage('Hello', {
|
|
194
233
|
tools: 'none',
|
|
195
234
|
useCaseMessage: 'You are helping a new customer onboard.',
|
|
235
|
+
conversationMemoryMaxTurns: 6,
|
|
196
236
|
});
|
|
197
237
|
```
|
|
198
238
|
|
|
@@ -209,6 +249,11 @@ await client.sendMessage('Hello', {
|
|
|
209
249
|
| `error` | `string \| undefined` | Safe user-facing error text. | `Request timed out before completion.` |
|
|
210
250
|
| `lastError` | `ClientErrorInfo \| undefined` | Structured internal error details. | `{ code: 'TIMEOUT', ... }` |
|
|
211
251
|
| `useCaseId` | `string` | Use case currently bound to the conversation. | `uc-default-1lji40` |
|
|
252
|
+
| `workflowId` | `string \| undefined` | Latest workflow run id returned by a V2 workflow use case. | `wf_123` |
|
|
253
|
+
| `workflowStatus` | `string \| undefined` | Latest workflow status returned by the orchestrator. | `completed` |
|
|
254
|
+
| `checkpointPrompt` | `string \| undefined` | Latest checkpoint prompt returned by a workflow use case. | `Review before continuing?` |
|
|
255
|
+
|
|
256
|
+
Checkpoint action UI is not included in the beta React widget. Consumers can inspect workflow metadata from state/events and add explicit continue/revise/cancel controls in a later integration.
|
|
212
257
|
|
|
213
258
|
## Client Events
|
|
214
259
|
|
|
@@ -239,24 +284,34 @@ await client.sendMessage('Hello', {
|
|
|
239
284
|
|
|
240
285
|
## Orchestrator Request Shape
|
|
241
286
|
|
|
242
|
-
The core client posts a canonical request to `/
|
|
287
|
+
The core client posts a canonical request to `/v2/chat`.
|
|
243
288
|
|
|
244
289
|
| Field | Type / values | Required | What it does | Example |
|
|
245
290
|
|---|---|---:|---|---|
|
|
246
291
|
| `use_case_id` | string | yes | Runtime use case identifier. | `uc-default-1lji40` |
|
|
247
292
|
| `stream` | `boolean` | no | Requests SSE when `true`. | `true` |
|
|
248
|
-
| `messages` | array | yes |
|
|
293
|
+
| `messages` | array | yes | Current user message, plus optional `useCaseMessage` system instruction. Server-side memory comes from `conversation_id`. | `[{ role: 'user', content: 'Hello' }]` |
|
|
249
294
|
| `tools` | `'none' \| 'all' \| string[]` | no | Limits which tools the orchestrator may use. | `['search_docs']` |
|
|
250
295
|
| `snapshot_id` | string | no | Pins the request to a published orchestration snapshot. | `snapshot_2026_04_14` |
|
|
296
|
+
| `runtime_version` | `'v2'` | yes | Runtime contract marker sent by the client. | `v2` |
|
|
297
|
+
| `user_id` | string | yes | User id for ownership and conversation memory. | `visitor_abc123` |
|
|
298
|
+
| `conversation_id` | string | yes | Durable conversation id. | `conv-homepage-001` |
|
|
299
|
+
| `session_id` | string | no | Backward-compatible mirror of `conversation_id` during migration. | `conv-homepage-001` |
|
|
300
|
+
| `conversation_title` | string | no | Optional conversation title. | `Homepage assistant` |
|
|
301
|
+
| `conversation_memory_max_turns` | number | no | Server-side memory window. | `6` |
|
|
251
302
|
|
|
252
303
|
Example:
|
|
253
304
|
|
|
254
305
|
```ts
|
|
255
306
|
{
|
|
256
307
|
use_case_id: 'uc-default-1lji40',
|
|
308
|
+
runtime_version: 'v2',
|
|
309
|
+
user_id: 'visitor_abc123',
|
|
310
|
+
conversation_id: 'conv-homepage-001',
|
|
311
|
+
session_id: 'conv-homepage-001',
|
|
312
|
+
conversation_memory_max_turns: 6,
|
|
257
313
|
stream: true,
|
|
258
314
|
messages: [
|
|
259
|
-
{ role: 'system', content: 'Keep it short and direct.' },
|
|
260
315
|
{ role: 'user', content: 'How do I get started?' },
|
|
261
316
|
],
|
|
262
317
|
tools: 'none',
|
|
@@ -301,7 +356,7 @@ import type { OrchestratorTransport } from '@tagit/ai-client-core';
|
|
|
301
356
|
|
|
302
357
|
const transport: OrchestratorTransport = {
|
|
303
358
|
async send(request, context) {
|
|
304
|
-
return fetch(`${context.baseUrl}
|
|
359
|
+
return fetch(`${context.baseUrl}${context.endpointPath}`, {
|
|
305
360
|
method: 'POST',
|
|
306
361
|
headers: context.headers,
|
|
307
362
|
body: JSON.stringify(request),
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClientConfig, ClientEventListener, ConversationState } from './types.js';
|
|
1
|
+
import type { ClientConfig, ClientEventListener, ConversationState, SendMessageOptions } from './types.js';
|
|
2
2
|
import { type OrchestratorTransport } from './transport.js';
|
|
3
3
|
/**
|
|
4
4
|
* Headless AI client core
|
|
@@ -34,6 +34,19 @@ export declare class AIClient {
|
|
|
34
34
|
* Check if history exceeds max messages and emit warning event
|
|
35
35
|
*/
|
|
36
36
|
private checkAndTrimHistory;
|
|
37
|
+
private getEndpointPath;
|
|
38
|
+
private buildRequestMessages;
|
|
39
|
+
resolveUserId(options?: Pick<SendMessageOptions, 'userId'>): string;
|
|
40
|
+
resolveConversationId(options?: Pick<SendMessageOptions, 'conversationId'>): string;
|
|
41
|
+
hasConfiguredConversationId(): boolean;
|
|
42
|
+
getResolvedIdentity(options?: Pick<SendMessageOptions, 'userId' | 'conversationId'>): {
|
|
43
|
+
userId: string;
|
|
44
|
+
conversationId: string;
|
|
45
|
+
entityId: string;
|
|
46
|
+
entityidType: string;
|
|
47
|
+
entityidAlgo: string;
|
|
48
|
+
entityidOwner: string;
|
|
49
|
+
};
|
|
37
50
|
/**
|
|
38
51
|
* Truncate error details to prevent massive error payloads
|
|
39
52
|
*/
|
|
@@ -41,10 +54,7 @@ export declare class AIClient {
|
|
|
41
54
|
/**
|
|
42
55
|
* Send a message and stream the response
|
|
43
56
|
*/
|
|
44
|
-
sendMessage(userMessage: string, options?:
|
|
45
|
-
tools?: 'none' | 'all' | string[];
|
|
46
|
-
useCaseMessage?: string;
|
|
47
|
-
}): Promise<void>;
|
|
57
|
+
sendMessage(userMessage: string, options?: SendMessageOptions): Promise<void>;
|
|
48
58
|
private streamResponseWithRetry;
|
|
49
59
|
/**
|
|
50
60
|
* Stream response from orchestrator
|
|
@@ -65,6 +75,7 @@ export declare class AIClient {
|
|
|
65
75
|
private appendAssistantDelta;
|
|
66
76
|
private applyTerminalContent;
|
|
67
77
|
private handleNonStreamingResponse;
|
|
78
|
+
private applyWorkflowDetails;
|
|
68
79
|
private extractAssistantText;
|
|
69
80
|
private shouldAcceptDegradedCompletion;
|
|
70
81
|
private hasAssistantContent;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EAEZ,mBAAmB,EACnB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EAEZ,mBAAmB,EACnB,iBAAiB,EAMjB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AASxB;;;GAGG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,SAAS,CAAwB;gBAGvC,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,qBAAqB,CAAC;KACnC;IAiBH;;OAEG;IACH,QAAQ,IAAI,iBAAiB;IAI7B;;OAEG;IACH,EAAE,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,IAAI;IAO7C;;OAEG;IACH,OAAO,CAAC,IAAI;IAUZ;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,oBAAoB;IAwB5B,aAAa,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GAAG,MAAM;IAYnE,qBAAqB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,GAAG,MAAM;IASnF,2BAA2B,IAAI,OAAO;IAItC,mBAAmB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,gBAAgB,CAAC,GAAG;QACpF,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB;IAYD;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAmB5B;;OAEG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,IAAI,CAAC;YAuDF,uBAAuB;IAyCrC;;OAEG;YACW,cAAc;IA0M5B;;OAEG;YACW,WAAW;IAqHzB;;;OAGG;YACW,mBAAmB;IA0BjC,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,oBAAoB;YA0Bd,0BAA0B;IA2CxC,OAAO,CAAC,oBAAoB;IA+C5B,OAAO,CAAC,oBAAoB;IAkC5B,OAAO,CAAC,8BAA8B;IAgBtC,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,0BAA0B;IAUlC,OAAO,CAAC,mBAAmB;IA+C3B;;OAEG;YACW,kBAAkB;IAkEhC;;OAEG;IACH,MAAM,IAAI,IAAI;IAUd;;;OAGG;IACH,YAAY,IAAI,IAAI;IAmBpB;;;;;OAKG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAWpD;AAED,eAAe,QAAQ,CAAC"}
|
package/dist/client.js
CHANGED
|
@@ -50,8 +50,11 @@ export class AIClient {
|
|
|
50
50
|
});
|
|
51
51
|
this.config = validateConfig(config);
|
|
52
52
|
this.transport = options?.transport ?? new FetchOrchestratorTransport();
|
|
53
|
+
const initialConversationId = this.config.conversation?.conversationId?.trim() ||
|
|
54
|
+
this.config.collector?.sessionId?.trim() ||
|
|
55
|
+
generateId();
|
|
53
56
|
this.conversationState = {
|
|
54
|
-
id:
|
|
57
|
+
id: initialConversationId,
|
|
55
58
|
messages: [],
|
|
56
59
|
isLoading: false,
|
|
57
60
|
isStreaming: false,
|
|
@@ -111,6 +114,56 @@ export class AIClient {
|
|
|
111
114
|
});
|
|
112
115
|
}
|
|
113
116
|
}
|
|
117
|
+
getEndpointPath() {
|
|
118
|
+
return '/v2/chat';
|
|
119
|
+
}
|
|
120
|
+
buildRequestMessages(useCaseMessage) {
|
|
121
|
+
const allMessages = this.conversationState.messages.map((msg) => ({
|
|
122
|
+
role: msg.role,
|
|
123
|
+
content: msg.content,
|
|
124
|
+
}));
|
|
125
|
+
let requestMessages = allMessages.slice(-1);
|
|
126
|
+
const trimmedUseCaseMessage = useCaseMessage?.trim();
|
|
127
|
+
if (trimmedUseCaseMessage) {
|
|
128
|
+
requestMessages = [
|
|
129
|
+
{
|
|
130
|
+
role: 'system',
|
|
131
|
+
content: trimmedUseCaseMessage,
|
|
132
|
+
},
|
|
133
|
+
...requestMessages,
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
return requestMessages;
|
|
137
|
+
}
|
|
138
|
+
resolveUserId(options) {
|
|
139
|
+
const user = this.config.user;
|
|
140
|
+
return (options?.userId?.trim() ||
|
|
141
|
+
user?.userId?.trim() ||
|
|
142
|
+
user?.entityId?.trim() ||
|
|
143
|
+
this.config.collector?.userId?.trim() ||
|
|
144
|
+
this.config.collector?.entityId?.trim() ||
|
|
145
|
+
this.conversationState.id);
|
|
146
|
+
}
|
|
147
|
+
resolveConversationId(options) {
|
|
148
|
+
return (options?.conversationId?.trim() ||
|
|
149
|
+
this.config.conversation?.conversationId?.trim() ||
|
|
150
|
+
this.config.collector?.sessionId?.trim() ||
|
|
151
|
+
this.conversationState.id);
|
|
152
|
+
}
|
|
153
|
+
hasConfiguredConversationId() {
|
|
154
|
+
return Boolean(this.config.conversation?.conversationId?.trim());
|
|
155
|
+
}
|
|
156
|
+
getResolvedIdentity(options) {
|
|
157
|
+
const userId = this.resolveUserId(options);
|
|
158
|
+
return {
|
|
159
|
+
userId,
|
|
160
|
+
conversationId: this.resolveConversationId(options),
|
|
161
|
+
entityId: this.config.user?.entityId?.trim() || this.config.collector?.entityId?.trim() || userId,
|
|
162
|
+
entityidType: this.config.user?.entityidType?.trim() || this.config.collector?.entityidType?.trim() || 'internal_user_id',
|
|
163
|
+
entityidAlgo: this.config.user?.entityidAlgo?.trim() || this.config.collector?.entityidAlgo?.trim() || 'native',
|
|
164
|
+
entityidOwner: this.config.user?.entityidOwner?.trim() || this.config.collector?.entityidOwner?.trim() || 'tagit-ai-client',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
114
167
|
/**
|
|
115
168
|
* Truncate error details to prevent massive error payloads
|
|
116
169
|
*/
|
|
@@ -156,11 +209,11 @@ export class AIClient {
|
|
|
156
209
|
this.conversationState.isLoading = true;
|
|
157
210
|
this.emit({ type: 'message_added', message: userMsg });
|
|
158
211
|
try {
|
|
159
|
-
await this.streamResponseWithRetry(options
|
|
212
|
+
await this.streamResponseWithRetry(options);
|
|
160
213
|
// Emit collector event if configured
|
|
161
214
|
if (this.config.collector?.enabled) {
|
|
162
215
|
const assistantMsg = this.findLatestAssistantMessage();
|
|
163
|
-
await this.emitCollectorEvent(userMsg, assistantMsg ?? null);
|
|
216
|
+
await this.emitCollectorEvent(userMsg, assistantMsg ?? null, options);
|
|
164
217
|
}
|
|
165
218
|
}
|
|
166
219
|
catch (error) {
|
|
@@ -183,13 +236,13 @@ export class AIClient {
|
|
|
183
236
|
this.emit({ type: 'state_synced' });
|
|
184
237
|
}
|
|
185
238
|
}
|
|
186
|
-
async streamResponseWithRetry(
|
|
239
|
+
async streamResponseWithRetry(options) {
|
|
187
240
|
const retries = this.config.orchestrator.maxRetries ?? 0;
|
|
188
241
|
const maxAttempts = Math.max(1, retries + 1);
|
|
189
242
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
190
243
|
const baselineLength = this.conversationState.messages.length;
|
|
191
244
|
try {
|
|
192
|
-
await this.streamResponse(
|
|
245
|
+
await this.streamResponse(options);
|
|
193
246
|
return;
|
|
194
247
|
}
|
|
195
248
|
catch (error) {
|
|
@@ -219,32 +272,30 @@ export class AIClient {
|
|
|
219
272
|
/**
|
|
220
273
|
* Stream response from orchestrator
|
|
221
274
|
*/
|
|
222
|
-
async streamResponse(
|
|
223
|
-
const requestMessages = this.
|
|
224
|
-
|
|
225
|
-
content: msg.content,
|
|
226
|
-
}));
|
|
227
|
-
const trimmedUseCaseMessage = useCaseMessage?.trim();
|
|
228
|
-
if (trimmedUseCaseMessage) {
|
|
229
|
-
requestMessages.unshift({
|
|
230
|
-
role: 'system',
|
|
231
|
-
content: trimmedUseCaseMessage,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
275
|
+
async streamResponse(options) {
|
|
276
|
+
const requestMessages = this.buildRequestMessages(options?.useCaseMessage);
|
|
277
|
+
const endpointPath = this.getEndpointPath();
|
|
234
278
|
const request = {
|
|
235
|
-
use_case_id: this.config.orchestrator.useCaseId,
|
|
279
|
+
use_case_id: options?.useCaseId?.trim() || this.config.orchestrator.useCaseId,
|
|
236
280
|
stream: this.config.ui?.streaming ?? true,
|
|
237
281
|
messages: requestMessages,
|
|
238
282
|
};
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
283
|
+
request.runtime_version = 'v2';
|
|
284
|
+
request.conversation_id = this.resolveConversationId(options);
|
|
285
|
+
request.session_id = request.conversation_id;
|
|
286
|
+
request.user_id = this.resolveUserId(options);
|
|
287
|
+
request.conversation_title = options?.conversationTitle ?? this.config.conversation?.title;
|
|
288
|
+
request.conversation_memory_max_turns = options?.conversationMemoryMaxTurns ?? this.config.conversation?.memoryMaxTurns;
|
|
289
|
+
if (options?.tools) {
|
|
290
|
+
request.tools = options.tools;
|
|
291
|
+
}
|
|
292
|
+
const snapshotId = options?.snapshotId?.trim() || this.config.orchestrator.snapshotId;
|
|
293
|
+
if (snapshotId) {
|
|
294
|
+
request.snapshot_id = snapshotId;
|
|
244
295
|
}
|
|
245
296
|
this.emit({
|
|
246
297
|
type: 'orchestrator_request',
|
|
247
|
-
endpoint: `${this.config.orchestrator.baseUrl}
|
|
298
|
+
endpoint: `${this.config.orchestrator.baseUrl.replace(/\/+$/, '')}${endpointPath}`,
|
|
248
299
|
request,
|
|
249
300
|
});
|
|
250
301
|
this.abortReason = null;
|
|
@@ -265,6 +316,7 @@ export class AIClient {
|
|
|
265
316
|
try {
|
|
266
317
|
const response = await this.transport.send(request, {
|
|
267
318
|
baseUrl: this.config.orchestrator.baseUrl,
|
|
319
|
+
endpointPath,
|
|
268
320
|
headers,
|
|
269
321
|
signal: this.abortController.signal,
|
|
270
322
|
});
|
|
@@ -475,6 +527,7 @@ export class AIClient {
|
|
|
475
527
|
const text = this.extractAssistantText(chunk.data) ?? '';
|
|
476
528
|
const lastMsg = this.conversationState.messages[this.conversationState.messages.length - 1];
|
|
477
529
|
if (text.trim() && lastMsg?.role === 'assistant' && !lastMsg.content.trim()) {
|
|
530
|
+
this.applyWorkflowDetails(chunk.data);
|
|
478
531
|
// Server batched the full response with no deltas — animate it in.
|
|
479
532
|
this.emit({ type: 'first_token', messageId: lastMsg.id });
|
|
480
533
|
await this.simulateDeltaStream(text, lastMsg.id);
|
|
@@ -576,6 +629,7 @@ export class AIClient {
|
|
|
576
629
|
return target.id;
|
|
577
630
|
}
|
|
578
631
|
applyTerminalContent(data) {
|
|
632
|
+
this.applyWorkflowDetails(data);
|
|
579
633
|
const text = this.extractAssistantText(data) ?? '';
|
|
580
634
|
if (!text.trim()) {
|
|
581
635
|
return;
|
|
@@ -614,6 +668,7 @@ export class AIClient {
|
|
|
614
668
|
if (contentType.includes('application/json') || raw.trim().startsWith('{')) {
|
|
615
669
|
try {
|
|
616
670
|
const parsed = JSON.parse(raw);
|
|
671
|
+
this.applyWorkflowDetails(parsed);
|
|
617
672
|
const extracted = this.extractAssistantText(parsed);
|
|
618
673
|
assistantText = extracted ?? raw;
|
|
619
674
|
}
|
|
@@ -633,6 +688,48 @@ export class AIClient {
|
|
|
633
688
|
this.emit({ type: 'first_token', messageId: assistantMsg.id });
|
|
634
689
|
}
|
|
635
690
|
}
|
|
691
|
+
applyWorkflowDetails(payload) {
|
|
692
|
+
const details = payload.details;
|
|
693
|
+
if (!details || typeof details !== 'object') {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const workflow = details.workflow;
|
|
697
|
+
if (!workflow || typeof workflow !== 'object') {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const workflowDetails = workflow;
|
|
701
|
+
const conversationId = typeof workflowDetails.conversationId === 'string'
|
|
702
|
+
? workflowDetails.conversationId.trim()
|
|
703
|
+
: undefined;
|
|
704
|
+
const workflowId = typeof workflowDetails.workflowId === 'string'
|
|
705
|
+
? workflowDetails.workflowId.trim()
|
|
706
|
+
: undefined;
|
|
707
|
+
const status = typeof workflowDetails.status === 'string'
|
|
708
|
+
? workflowDetails.status.trim()
|
|
709
|
+
: undefined;
|
|
710
|
+
const checkpointPrompt = typeof workflowDetails.checkpointPrompt === 'string'
|
|
711
|
+
? workflowDetails.checkpointPrompt
|
|
712
|
+
: undefined;
|
|
713
|
+
if (conversationId && conversationId !== this.conversationState.id) {
|
|
714
|
+
this.conversationState.id = conversationId;
|
|
715
|
+
}
|
|
716
|
+
if (workflowId) {
|
|
717
|
+
this.conversationState.workflowId = workflowId;
|
|
718
|
+
}
|
|
719
|
+
if (status) {
|
|
720
|
+
this.conversationState.workflowStatus = status;
|
|
721
|
+
}
|
|
722
|
+
if (checkpointPrompt !== undefined) {
|
|
723
|
+
this.conversationState.checkpointPrompt = checkpointPrompt;
|
|
724
|
+
}
|
|
725
|
+
this.emit({
|
|
726
|
+
type: 'workflow_metadata_updated',
|
|
727
|
+
conversationId: conversationId || this.conversationState.id,
|
|
728
|
+
workflowId,
|
|
729
|
+
status,
|
|
730
|
+
checkpointPrompt,
|
|
731
|
+
});
|
|
732
|
+
}
|
|
636
733
|
extractAssistantText(payload) {
|
|
637
734
|
const messages = payload.messages;
|
|
638
735
|
if (Array.isArray(messages)) {
|
|
@@ -722,16 +819,18 @@ export class AIClient {
|
|
|
722
819
|
}
|
|
723
820
|
return undefined;
|
|
724
821
|
}
|
|
725
|
-
buildCollectorEvent(userMessage, assistantMessage) {
|
|
822
|
+
buildCollectorEvent(userMessage, assistantMessage, options) {
|
|
726
823
|
const collector = this.config.collector;
|
|
727
|
-
const
|
|
728
|
-
const
|
|
729
|
-
const
|
|
730
|
-
const
|
|
731
|
-
const
|
|
732
|
-
const
|
|
824
|
+
const resolved = this.getResolvedIdentity(options);
|
|
825
|
+
const userId = resolved.userId;
|
|
826
|
+
const requestUseCaseId = options?.useCaseId?.trim() || this.config.orchestrator.useCaseId;
|
|
827
|
+
const agentId = collector.agentId ?? requestUseCaseId;
|
|
828
|
+
const entityId = resolved.entityId;
|
|
829
|
+
const entityidType = resolved.entityidType;
|
|
830
|
+
const entityidAlgo = resolved.entityidAlgo;
|
|
831
|
+
const entityidOwner = resolved.entityidOwner;
|
|
733
832
|
const acceptCookies = collector.acceptCookies ?? false;
|
|
734
|
-
const eventSubContext = collector.eventSubContext ??
|
|
833
|
+
const eventSubContext = collector.eventSubContext ?? requestUseCaseId;
|
|
735
834
|
return {
|
|
736
835
|
tagId: collector.tagId,
|
|
737
836
|
tagType: 'web-sdk-nativeId',
|
|
@@ -752,7 +851,7 @@ export class AIClient {
|
|
|
752
851
|
contentData: {
|
|
753
852
|
userId,
|
|
754
853
|
agentId,
|
|
755
|
-
conversationId:
|
|
854
|
+
conversationId: resolved.conversationId,
|
|
756
855
|
userMessageId: userMessage.id,
|
|
757
856
|
agentMessageId: assistantMessage?.id ?? null,
|
|
758
857
|
userMessage: userMessage.content,
|
|
@@ -764,12 +863,12 @@ export class AIClient {
|
|
|
764
863
|
/**
|
|
765
864
|
* Emit a conversation event to the TagIt collector
|
|
766
865
|
*/
|
|
767
|
-
async emitCollectorEvent(userMessage, assistantMessage) {
|
|
866
|
+
async emitCollectorEvent(userMessage, assistantMessage, options) {
|
|
768
867
|
if (!this.config.collector?.baseUrl || !this.config.collector?.tagId) {
|
|
769
868
|
return;
|
|
770
869
|
}
|
|
771
870
|
const endpoint = this.config.collector.baseUrl;
|
|
772
|
-
const event = this.buildCollectorEvent(userMessage, assistantMessage);
|
|
871
|
+
const event = this.buildCollectorEvent(userMessage, assistantMessage, options);
|
|
773
872
|
try {
|
|
774
873
|
const MAX_PAYLOAD_SIZE = 16 * 1024;
|
|
775
874
|
const body = JSON.stringify(event);
|
|
@@ -845,7 +944,9 @@ export class AIClient {
|
|
|
845
944
|
});
|
|
846
945
|
}
|
|
847
946
|
this.conversationState.messages = [];
|
|
848
|
-
this.
|
|
947
|
+
if (!this.hasConfiguredConversationId()) {
|
|
948
|
+
this.conversationState.id = generateId();
|
|
949
|
+
}
|
|
849
950
|
this.conversationState.error = undefined;
|
|
850
951
|
this.conversationState.lastError = undefined;
|
|
851
952
|
this.conversationState.isStreaming = false;
|