10xgraph-client 0.0.0-stage → 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/CHANGELOG.md +276 -0
- package/LICENSE +21 -0
- package/README.md +400 -2
- package/dist/agent.d.ts +22 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/client.d.ts +479 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/compat.d.ts +28 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/endpoints/addThreadMessages.d.ts +22 -0
- package/dist/endpoints/addThreadMessages.d.ts.map +1 -0
- package/dist/endpoints/clearThreadState.d.ts +15 -0
- package/dist/endpoints/clearThreadState.d.ts.map +1 -0
- package/dist/endpoints/deleteMemory.d.ts +19 -0
- package/dist/endpoints/deleteMemory.d.ts.map +1 -0
- package/dist/endpoints/deleteThread.d.ts +19 -0
- package/dist/endpoints/deleteThread.d.ts.map +1 -0
- package/dist/endpoints/deleteThreadMessage.d.ts +20 -0
- package/dist/endpoints/deleteThreadMessage.d.ts.map +1 -0
- package/dist/endpoints/files.d.ts +83 -0
- package/dist/endpoints/files.d.ts.map +1 -0
- package/dist/endpoints/fixGraph.d.ts +29 -0
- package/dist/endpoints/fixGraph.d.ts.map +1 -0
- package/dist/endpoints/forgetMemories.d.ts +22 -0
- package/dist/endpoints/forgetMemories.d.ts.map +1 -0
- package/dist/endpoints/getMemory.d.ts +19 -0
- package/dist/endpoints/getMemory.d.ts.map +1 -0
- package/dist/endpoints/graph.d.ts +39 -0
- package/dist/endpoints/graph.d.ts.map +1 -0
- package/dist/endpoints/graphTools.d.ts +32 -0
- package/dist/endpoints/graphTools.d.ts.map +1 -0
- package/dist/endpoints/invoke.d.ts +59 -0
- package/dist/endpoints/invoke.d.ts.map +1 -0
- package/dist/endpoints/listMemories.d.ts +19 -0
- package/dist/endpoints/listMemories.d.ts.map +1 -0
- package/dist/endpoints/metadata.d.ts +6 -0
- package/dist/endpoints/metadata.d.ts.map +1 -0
- package/dist/endpoints/observability.d.ts +58 -0
- package/dist/endpoints/observability.d.ts.map +1 -0
- package/dist/endpoints/ping.d.ts +10 -0
- package/dist/endpoints/ping.d.ts.map +1 -0
- package/dist/endpoints/realtime.d.ts +134 -0
- package/dist/endpoints/realtime.d.ts.map +1 -0
- package/dist/endpoints/searchMemory.d.ts +57 -0
- package/dist/endpoints/searchMemory.d.ts.map +1 -0
- package/dist/endpoints/stateSchema.d.ts +50 -0
- package/dist/endpoints/stateSchema.d.ts.map +1 -0
- package/dist/endpoints/stopGraph.d.ts +26 -0
- package/dist/endpoints/stopGraph.d.ts.map +1 -0
- package/dist/endpoints/storeMemory.d.ts +33 -0
- package/dist/endpoints/storeMemory.d.ts.map +1 -0
- package/dist/endpoints/stream.d.ts +60 -0
- package/dist/endpoints/stream.d.ts.map +1 -0
- package/dist/endpoints/threadDetails.d.ts +19 -0
- package/dist/endpoints/threadDetails.d.ts.map +1 -0
- package/dist/endpoints/threadMessage.d.ts +17 -0
- package/dist/endpoints/threadMessage.d.ts.map +1 -0
- package/dist/endpoints/threadMessages.d.ts +23 -0
- package/dist/endpoints/threadMessages.d.ts.map +1 -0
- package/dist/endpoints/threadState.d.ts +14 -0
- package/dist/endpoints/threadState.d.ts.map +1 -0
- package/dist/endpoints/threads.d.ts +30 -0
- package/dist/endpoints/threads.d.ts.map +1 -0
- package/dist/endpoints/updateMemory.d.ts +21 -0
- package/dist/endpoints/updateMemory.d.ts.map +1 -0
- package/dist/endpoints/updateThreadState.d.ts +18 -0
- package/dist/endpoints/updateThreadState.d.ts.map +1 -0
- package/dist/endpoints/wsStream.d.ts +33 -0
- package/dist/endpoints/wsStream.d.ts.map +1 -0
- package/dist/errors.d.ts +155 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2664 -0
- package/dist/index.js.map +1 -0
- package/dist/message.d.ts +174 -0
- package/dist/message.d.ts.map +1 -0
- package/dist/request.d.ts +33 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/ws.d.ts +48 -0
- package/dist/ws.d.ts.map +1 -0
- package/package.json +99 -4
- package/src/agent.ts +43 -0
- package/src/client.ts +1044 -0
- package/src/compat.ts +35 -0
- package/src/endpoints/addThreadMessages.ts +83 -0
- package/src/endpoints/clearThreadState.ts +75 -0
- package/src/endpoints/deleteMemory.ts +91 -0
- package/src/endpoints/deleteThread.ts +78 -0
- package/src/endpoints/deleteThreadMessage.ts +83 -0
- package/src/endpoints/files.ts +274 -0
- package/src/endpoints/fixGraph.ts +91 -0
- package/src/endpoints/forgetMemories.ts +95 -0
- package/src/endpoints/getMemory.ts +82 -0
- package/src/endpoints/graph.ts +97 -0
- package/src/endpoints/graphTools.ts +90 -0
- package/src/endpoints/invoke.ts +318 -0
- package/src/endpoints/listMemories.ts +97 -0
- package/src/endpoints/metadata.ts +6 -0
- package/src/endpoints/observability.ts +107 -0
- package/src/endpoints/ping.ts +58 -0
- package/src/endpoints/realtime.ts +326 -0
- package/src/endpoints/searchMemory.ts +132 -0
- package/src/endpoints/stateSchema.ts +106 -0
- package/src/endpoints/stopGraph.ts +85 -0
- package/src/endpoints/storeMemory.ts +98 -0
- package/src/endpoints/stream.ts +440 -0
- package/src/endpoints/threadDetails.ts +78 -0
- package/src/endpoints/threadMessage.ts +76 -0
- package/src/endpoints/threadMessages.ts +97 -0
- package/src/endpoints/threadState.ts +74 -0
- package/src/endpoints/threads.ts +112 -0
- package/src/endpoints/updateMemory.ts +90 -0
- package/src/endpoints/updateThreadState.ts +82 -0
- package/src/endpoints/wsStream.ts +251 -0
- package/src/errors.ts +553 -0
- package/src/index.ts +108 -0
- package/src/message.ts +436 -0
- package/src/request.ts +168 -0
- package/src/tools.ts +225 -0
- package/src/ws.ts +100 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { ResponseMetadata } from './metadata.js';
|
|
2
|
+
import { createErrorFromResponse } from '../errors.js';
|
|
3
|
+
import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
|
|
4
|
+
|
|
5
|
+
export interface StoreMemoryContext extends RequestContext {}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Types of memories that can be stored
|
|
9
|
+
*/
|
|
10
|
+
export enum MemoryType {
|
|
11
|
+
EPISODIC = 'episodic', // Conversation memories
|
|
12
|
+
SEMANTIC = 'semantic', // Facts and knowledge
|
|
13
|
+
PROCEDURAL = 'procedural', // How-to knowledge
|
|
14
|
+
ENTITY = 'entity', // Entity-based memories
|
|
15
|
+
RELATIONSHIP = 'relationship', // Entity relationships
|
|
16
|
+
CUSTOM = 'custom', // Custom memory types
|
|
17
|
+
DECLARATIVE = 'declarative', // Explicit facts and events
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface StoreMemoryRequest {
|
|
21
|
+
config?: Record<string, any>;
|
|
22
|
+
options?: Record<string, any>;
|
|
23
|
+
content: string;
|
|
24
|
+
memory_type: MemoryType;
|
|
25
|
+
category: string;
|
|
26
|
+
metadata?: Record<string, any>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StoreMemoryData {
|
|
30
|
+
memory_id: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface StoreMemoryResponse {
|
|
34
|
+
data: StoreMemoryData;
|
|
35
|
+
metadata: ResponseMetadata;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function storeMemory(
|
|
39
|
+
context: StoreMemoryContext,
|
|
40
|
+
request: StoreMemoryRequest
|
|
41
|
+
): Promise<StoreMemoryResponse> {
|
|
42
|
+
try {
|
|
43
|
+
if (context.debug) {
|
|
44
|
+
console.debug('TenxGraphClient: Storing memory with type', request.memory_type);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timeoutId = setTimeout(() => controller.abort(), context.timeout);
|
|
49
|
+
|
|
50
|
+
const url = `${context.baseUrl}/v1/store/memories`;
|
|
51
|
+
|
|
52
|
+
// Prepare request body
|
|
53
|
+
const body = {
|
|
54
|
+
config: request.config || {},
|
|
55
|
+
options: request.options || {},
|
|
56
|
+
content: request.content,
|
|
57
|
+
memory_type: request.memory_type,
|
|
58
|
+
category: request.category,
|
|
59
|
+
metadata: request.metadata || {},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const response = await fetch(url, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: buildHeaders(context as RequestContext, {
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
}),
|
|
67
|
+
...getRequestCredentials(context as RequestContext),
|
|
68
|
+
body: JSON.stringify(body),
|
|
69
|
+
signal: controller.signal,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
clearTimeout(timeoutId);
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
console.warn(`TenxGraphClient: Store memory failed with HTTP ${response.status}`);
|
|
76
|
+
const error = await createErrorFromResponse(
|
|
77
|
+
response,
|
|
78
|
+
'Store memory request failed',
|
|
79
|
+
'/v1/memory',
|
|
80
|
+
'POST'
|
|
81
|
+
);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const data: StoreMemoryResponse = await response.json();
|
|
86
|
+
|
|
87
|
+
if (context.debug) {
|
|
88
|
+
console.info('TenxGraphClient: Memory stored successfully', data);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return data;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (context.debug) {
|
|
94
|
+
console.debug('TenxGraphClient: Store memory failed:', error);
|
|
95
|
+
}
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { Message } from '../message.js';
|
|
2
|
+
import { AgentState } from '../agent.js';
|
|
3
|
+
import { ToolExecutor } from '../tools.js';
|
|
4
|
+
import { createErrorFromResponse } from '../errors.js';
|
|
5
|
+
import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
|
|
6
|
+
|
|
7
|
+
export interface StreamContext extends RequestContext {
|
|
8
|
+
toolExecutor?: ToolExecutor;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Stream Request payload (same as Invoke)
|
|
12
|
+
export interface StreamRequest {
|
|
13
|
+
messages: any[]; // Will be serialized Message objects
|
|
14
|
+
initial_state?: Record<string, any>;
|
|
15
|
+
config?: Record<string, any>;
|
|
16
|
+
recursion_limit?: number;
|
|
17
|
+
response_granularity?: 'full' | 'partial' | 'low';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Stream Events
|
|
21
|
+
export enum StreamEventType {
|
|
22
|
+
MESSAGE = 'message',
|
|
23
|
+
UPDATES = 'updates',
|
|
24
|
+
STATE = 'state',
|
|
25
|
+
ERROR = 'error',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Response metadata
|
|
29
|
+
export interface StreamMetadata {
|
|
30
|
+
is_new_thread: boolean;
|
|
31
|
+
thread_id: string;
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Individual stream chunk from server
|
|
36
|
+
export interface StreamChunk {
|
|
37
|
+
event: StreamEventType | string;
|
|
38
|
+
message?: Message | null;
|
|
39
|
+
state?: AgentState | null;
|
|
40
|
+
data?: any;
|
|
41
|
+
thread_id?: string;
|
|
42
|
+
run_id?: string;
|
|
43
|
+
metadata?: StreamMetadata | Record<string, any>;
|
|
44
|
+
timestamp?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Parse NDJSON stream and yield individual chunks immediately as they arrive
|
|
49
|
+
* Handles streaming response body as async iterable
|
|
50
|
+
* Supports both newline-separated and concatenated JSON objects
|
|
51
|
+
*/
|
|
52
|
+
async function* parseStreamChunks(
|
|
53
|
+
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
54
|
+
debug: boolean
|
|
55
|
+
): AsyncGenerator<StreamChunk, void, unknown> {
|
|
56
|
+
const decoder = new TextDecoder();
|
|
57
|
+
let buffer = '';
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
while (true) {
|
|
61
|
+
const { done, value } = await reader.read();
|
|
62
|
+
|
|
63
|
+
if (done) {
|
|
64
|
+
// Process any remaining data in buffer
|
|
65
|
+
if (buffer.trim()) {
|
|
66
|
+
yield* processBuffer(buffer, debug, true);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Append new data to buffer
|
|
72
|
+
buffer += decoder.decode(value, { stream: true });
|
|
73
|
+
|
|
74
|
+
// Immediately process and yield all complete chunks
|
|
75
|
+
const result = yield* processBuffer(buffer, debug, false);
|
|
76
|
+
buffer = result.remaining;
|
|
77
|
+
}
|
|
78
|
+
} finally {
|
|
79
|
+
reader.releaseLock();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Process buffer and yield all complete JSON chunks
|
|
85
|
+
* Returns the remaining incomplete buffer
|
|
86
|
+
*/
|
|
87
|
+
function* processBuffer(
|
|
88
|
+
buffer: string,
|
|
89
|
+
debug: boolean,
|
|
90
|
+
isFinal: boolean
|
|
91
|
+
): Generator<StreamChunk, { remaining: string }, unknown> {
|
|
92
|
+
// First, try to process newline-separated lines (NDJSON format)
|
|
93
|
+
while (buffer.includes('\n')) {
|
|
94
|
+
const newlineIndex = buffer.indexOf('\n');
|
|
95
|
+
const line = buffer.slice(0, newlineIndex).trim();
|
|
96
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
97
|
+
|
|
98
|
+
if (line) {
|
|
99
|
+
try {
|
|
100
|
+
const chunk = JSON.parse(line) as StreamChunk;
|
|
101
|
+
if (debug) {
|
|
102
|
+
console.debug('TenxGraphClient: Stream chunk received (NDJSON):', chunk.event);
|
|
103
|
+
}
|
|
104
|
+
yield chunk;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (debug) {
|
|
107
|
+
console.warn('TenxGraphClient: Failed to parse NDJSON line:', line.slice(0, 100), error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Then, try to extract concatenated JSON objects (no newlines)
|
|
114
|
+
while (buffer.trim()) {
|
|
115
|
+
const extracted = extractFirstJSON(buffer);
|
|
116
|
+
if (extracted) {
|
|
117
|
+
try {
|
|
118
|
+
const chunk = JSON.parse(extracted.json) as StreamChunk;
|
|
119
|
+
if (debug) {
|
|
120
|
+
console.debug('TenxGraphClient: Stream chunk received (concat):', chunk.event);
|
|
121
|
+
}
|
|
122
|
+
yield chunk;
|
|
123
|
+
buffer = extracted.remaining;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (debug) {
|
|
126
|
+
console.warn(
|
|
127
|
+
'TenxGraphClient: Failed to parse concatenated JSON:',
|
|
128
|
+
extracted.json.slice(0, 100),
|
|
129
|
+
error
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
// Incomplete JSON, keep in buffer
|
|
136
|
+
if (isFinal && buffer.trim()) {
|
|
137
|
+
// On final read, try to parse what's left
|
|
138
|
+
try {
|
|
139
|
+
const chunk = JSON.parse(buffer.trim()) as StreamChunk;
|
|
140
|
+
if (debug) {
|
|
141
|
+
console.debug('TenxGraphClient: Final stream chunk:', chunk.event);
|
|
142
|
+
}
|
|
143
|
+
yield chunk;
|
|
144
|
+
buffer = '';
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (debug) {
|
|
147
|
+
console.warn(
|
|
148
|
+
'TenxGraphClient: Failed to parse final buffer:',
|
|
149
|
+
buffer.slice(0, 100),
|
|
150
|
+
error
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { remaining: buffer };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Extract the first complete JSON object from a string that may contain multiple concatenated JSON objects
|
|
164
|
+
* Returns the JSON string and the remaining buffer
|
|
165
|
+
*/
|
|
166
|
+
function extractFirstJSON(buffer: string): { json: string; remaining: string } | null {
|
|
167
|
+
buffer = buffer.trim();
|
|
168
|
+
if (!buffer || buffer[0] !== '{') {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let depth = 0;
|
|
173
|
+
let inString = false;
|
|
174
|
+
let escaped = false;
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
177
|
+
const char = buffer[i];
|
|
178
|
+
|
|
179
|
+
if (escaped) {
|
|
180
|
+
escaped = false;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (char === '\\') {
|
|
185
|
+
escaped = true;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (char === '"') {
|
|
190
|
+
inString = !inString;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (inString) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (char === '{') {
|
|
199
|
+
depth++;
|
|
200
|
+
} else if (char === '}') {
|
|
201
|
+
depth--;
|
|
202
|
+
if (depth === 0) {
|
|
203
|
+
// Found complete JSON object
|
|
204
|
+
return {
|
|
205
|
+
json: buffer.slice(0, i + 1),
|
|
206
|
+
remaining: buffer.slice(i + 1).trim(),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Incomplete JSON object
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check if messages contain remote tool calls
|
|
218
|
+
*/
|
|
219
|
+
function hasRemoteToolCalls(messages: Message[]): boolean {
|
|
220
|
+
return messages.some(
|
|
221
|
+
(msg: Message) =>
|
|
222
|
+
msg.content && msg.content.some((block: any) => block.type === 'remote_tool_call')
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Stream invoke to the graph with automatic tool execution loop
|
|
228
|
+
* Returns an async iterable that yields stream chunks as they arrive
|
|
229
|
+
*
|
|
230
|
+
* This implements the same tool execution loop as invoke(), but uses
|
|
231
|
+
* HTTP streaming instead of REST API calls.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```ts
|
|
235
|
+
* const stream = streamInvoke(context, request);
|
|
236
|
+
* for await (const chunk of stream) {
|
|
237
|
+
* if (chunk.event === 'message') {
|
|
238
|
+
* console.log('Message:', chunk.message);
|
|
239
|
+
* } else if (chunk.event === 'updates') {
|
|
240
|
+
* console.log('State:', chunk.state);
|
|
241
|
+
* }
|
|
242
|
+
* }
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
export async function* streamInvoke(
|
|
246
|
+
context: StreamContext,
|
|
247
|
+
request: StreamRequest
|
|
248
|
+
): AsyncGenerator<StreamChunk, void, unknown> {
|
|
249
|
+
const recursion_limit = request.recursion_limit || 25;
|
|
250
|
+
|
|
251
|
+
if (context.debug) {
|
|
252
|
+
console.debug('TenxGraphClient: Starting stream invoke with recursion_limit:', recursion_limit);
|
|
253
|
+
console.debug('TenxGraphClient: Initial request:', JSON.stringify(request, null, 2));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let currentMessages = request.messages;
|
|
257
|
+
let iterations = 0;
|
|
258
|
+
const initial_state = request.initial_state;
|
|
259
|
+
const config = request.config;
|
|
260
|
+
const response_granularity = request.response_granularity;
|
|
261
|
+
|
|
262
|
+
// Main recursion loop (same as invoke, but using streaming)
|
|
263
|
+
while (iterations < recursion_limit) {
|
|
264
|
+
iterations++;
|
|
265
|
+
|
|
266
|
+
if (context.debug) {
|
|
267
|
+
console.debug(`TenxGraphClient: Stream iteration ${iterations}/${recursion_limit}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Prepare request for this iteration
|
|
271
|
+
const iterationRequest: StreamRequest = {
|
|
272
|
+
messages: currentMessages,
|
|
273
|
+
initial_state: iterations === 1 ? initial_state : undefined,
|
|
274
|
+
config,
|
|
275
|
+
recursion_limit,
|
|
276
|
+
response_granularity,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
if (context.debug) {
|
|
280
|
+
console.debug(
|
|
281
|
+
'TenxGraphClient: Stream request payload:',
|
|
282
|
+
JSON.stringify(iterationRequest, null, 2)
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Open the stream and yield all chunks
|
|
287
|
+
const controller = new AbortController();
|
|
288
|
+
const timeoutId = setTimeout(() => controller.abort(), context.timeout);
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
const response = await fetch(`${context.baseUrl}/v1/graph/stream`, {
|
|
292
|
+
method: 'POST',
|
|
293
|
+
headers: buildHeaders(context as RequestContext, {
|
|
294
|
+
'Content-Type': 'application/json',
|
|
295
|
+
Accept: 'application/x-ndjson, application/json',
|
|
296
|
+
}),
|
|
297
|
+
...getRequestCredentials(context as RequestContext),
|
|
298
|
+
body: JSON.stringify(iterationRequest),
|
|
299
|
+
signal: controller.signal,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
clearTimeout(timeoutId);
|
|
303
|
+
|
|
304
|
+
if (!response.ok) {
|
|
305
|
+
console.warn(`TenxGraphClient: Stream failed with HTTP ${response.status}`);
|
|
306
|
+
const error = await createErrorFromResponse(
|
|
307
|
+
response,
|
|
308
|
+
'Stream request failed',
|
|
309
|
+
'/v1/stream',
|
|
310
|
+
'POST'
|
|
311
|
+
);
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!response.body) {
|
|
316
|
+
throw new Error('No response body for stream');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Collect messages while yielding chunks
|
|
320
|
+
const responseMessages: Message[] = [];
|
|
321
|
+
const reader = response.body.getReader();
|
|
322
|
+
|
|
323
|
+
for await (const chunk of parseStreamChunks(reader, context.debug)) {
|
|
324
|
+
// Yield every chunk to the caller
|
|
325
|
+
yield chunk;
|
|
326
|
+
|
|
327
|
+
// Also collect messages for tool execution check
|
|
328
|
+
if (chunk.message) {
|
|
329
|
+
responseMessages.push(chunk.message);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (context.debug) {
|
|
334
|
+
console.info('TenxGraphClient: Stream iteration completed');
|
|
335
|
+
console.debug('TenxGraphClient: Received', responseMessages.length, 'messages');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Check if there are remote tool calls to execute
|
|
339
|
+
const hasToolCalls = hasRemoteToolCalls(responseMessages);
|
|
340
|
+
|
|
341
|
+
if (hasToolCalls && context.toolExecutor) {
|
|
342
|
+
if (context.debug) {
|
|
343
|
+
console.debug('TenxGraphClient: Found remote tool calls, executing...');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Execute all tool calls using the ToolExecutor
|
|
347
|
+
const toolResults = await context.toolExecutor.executeToolCalls(responseMessages);
|
|
348
|
+
|
|
349
|
+
if (context.debug) {
|
|
350
|
+
console.debug(`TenxGraphClient: Executed ${toolResults.length} tool calls`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Serialize tool results for next iteration
|
|
354
|
+
currentMessages = toolResults.map((msg) => serializeMessage(msg));
|
|
355
|
+
|
|
356
|
+
// Continue the loop with tool results
|
|
357
|
+
continue;
|
|
358
|
+
} else {
|
|
359
|
+
// No more tool calls, we're done
|
|
360
|
+
if (context.debug) {
|
|
361
|
+
console.debug('TenxGraphClient: No remote tool calls found, finishing');
|
|
362
|
+
}
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
} catch (error) {
|
|
366
|
+
clearTimeout(timeoutId);
|
|
367
|
+
|
|
368
|
+
if ((error as Error).name === 'AbortError') {
|
|
369
|
+
console.warn(`TenxGraphClient: Stream timeout after ${context.timeout}ms`);
|
|
370
|
+
throw new Error(`Request timeout after ${context.timeout}ms`);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
console.error('TenxGraphClient: Stream failed:', error);
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Check if we hit the recursion limit
|
|
379
|
+
if (iterations >= recursion_limit) {
|
|
380
|
+
if (context.debug) {
|
|
381
|
+
console.warn(`TenxGraphClient: Recursion limit of ${recursion_limit} reached`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (context.debug) {
|
|
386
|
+
console.debug(`TenxGraphClient: Stream invoke completed after ${iterations} iterations`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Clean content blocks by removing empty arrays and undefined values
|
|
392
|
+
* Server requires array of content blocks, not simplified string
|
|
393
|
+
*/
|
|
394
|
+
function cleanContent(content: any[]): any[] {
|
|
395
|
+
// Always return as array of blocks (server requires this format)
|
|
396
|
+
return content.map((block) => {
|
|
397
|
+
const cleaned: any = {};
|
|
398
|
+
|
|
399
|
+
for (const [key, value] of Object.entries(block)) {
|
|
400
|
+
// Skip empty arrays
|
|
401
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
// Skip undefined values
|
|
405
|
+
if (value === undefined) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
cleaned[key] = value;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return cleaned;
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Serialize a Message object for API transmission
|
|
417
|
+
*/
|
|
418
|
+
export function serializeMessage(message: Message): any {
|
|
419
|
+
const serialized: any = {
|
|
420
|
+
role: message.role,
|
|
421
|
+
content: cleanContent(message.content),
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// message_id: use "0" if not set (server will generate one)
|
|
425
|
+
if (message.message_id !== null && message.message_id !== undefined) {
|
|
426
|
+
serialized.message_id = String(message.message_id);
|
|
427
|
+
} else {
|
|
428
|
+
serialized.message_id = '0';
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Only include optional fields if they are explicitly set
|
|
432
|
+
if (message.tools_calls) {
|
|
433
|
+
serialized.tools_calls = message.tools_calls;
|
|
434
|
+
}
|
|
435
|
+
if (message.metadata && Object.keys(message.metadata).length > 0) {
|
|
436
|
+
serialized.metadata = message.metadata;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return serialized;
|
|
440
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ResponseMetadata } from './metadata.js';
|
|
2
|
+
import { createErrorFromResponse } from '../errors.js';
|
|
3
|
+
import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
|
|
4
|
+
|
|
5
|
+
export interface ThreadDetailsContext extends RequestContext {}
|
|
6
|
+
|
|
7
|
+
export interface ThreadDetailsData {
|
|
8
|
+
thread_data: {
|
|
9
|
+
thread: Record<string, any>;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ThreadDetailsResponse {
|
|
14
|
+
data: ThreadDetailsData;
|
|
15
|
+
metadata: ResponseMetadata;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fetch details for a specific thread.
|
|
20
|
+
* GET /v1/threads/{threadId}
|
|
21
|
+
*/
|
|
22
|
+
export async function threadDetails(
|
|
23
|
+
context: ThreadDetailsContext,
|
|
24
|
+
threadId: string | number
|
|
25
|
+
): Promise<ThreadDetailsResponse> {
|
|
26
|
+
try {
|
|
27
|
+
if (context.debug) {
|
|
28
|
+
console.debug(`TenxGraphClient: Fetching thread details for thread ${threadId}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timeoutId = setTimeout(() => controller.abort(), context.timeout);
|
|
33
|
+
|
|
34
|
+
const url = `${context.baseUrl}/v1/threads/${threadId}`;
|
|
35
|
+
|
|
36
|
+
const response = await fetch(url, {
|
|
37
|
+
method: 'GET',
|
|
38
|
+
headers: buildHeaders(context as RequestContext, {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
accept: 'application/json',
|
|
41
|
+
}),
|
|
42
|
+
...getRequestCredentials(context as RequestContext),
|
|
43
|
+
signal: controller.signal,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
clearTimeout(timeoutId);
|
|
47
|
+
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
console.warn(`TenxGraphClient: Thread details fetch failed with HTTP ${response.status}`);
|
|
50
|
+
const error = await createErrorFromResponse(
|
|
51
|
+
response,
|
|
52
|
+
'Thread details fetch failed',
|
|
53
|
+
'/v1/threads/{thread_id}',
|
|
54
|
+
'GET'
|
|
55
|
+
);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const data: ThreadDetailsResponse = await response.json();
|
|
60
|
+
|
|
61
|
+
if (context.debug) {
|
|
62
|
+
console.info('TenxGraphClient: Thread details fetched successfully', data);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return data;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if ((error as Error).name === 'AbortError') {
|
|
68
|
+
console.warn(`TenxGraphClient: Thread details fetch timeout after ${context.timeout}ms`);
|
|
69
|
+
throw new Error(`Request timeout after ${context.timeout}ms`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (context.debug) {
|
|
73
|
+
console.debug('TenxGraphClient: Thread details fetch failed:', error);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ResponseMetadata } from './metadata.js';
|
|
2
|
+
import { Message } from '../message.js';
|
|
3
|
+
import { createErrorFromResponse } from '../errors.js';
|
|
4
|
+
import { buildHeaders, getRequestCredentials, RequestContext } from '../request.js';
|
|
5
|
+
|
|
6
|
+
export interface ThreadMessageContext extends RequestContext {}
|
|
7
|
+
|
|
8
|
+
export interface ThreadMessageRequest {
|
|
9
|
+
threadId: string | number;
|
|
10
|
+
messageId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ThreadMessageData extends Message {
|
|
14
|
+
// This extends the Message class to ensure type compatibility
|
|
15
|
+
// The API returns a message object directly as data
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ThreadMessageResponse {
|
|
19
|
+
data: ThreadMessageData;
|
|
20
|
+
metadata: ResponseMetadata;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function threadMessage(
|
|
24
|
+
context: ThreadMessageContext,
|
|
25
|
+
request: ThreadMessageRequest
|
|
26
|
+
): Promise<ThreadMessageResponse> {
|
|
27
|
+
try {
|
|
28
|
+
if (context.debug) {
|
|
29
|
+
console.debug(
|
|
30
|
+
'TenxGraphClient: Fetching thread message',
|
|
31
|
+
`thread: ${request.threadId}`,
|
|
32
|
+
`message: ${request.messageId}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
const timeoutId = setTimeout(() => controller.abort(), context.timeout);
|
|
38
|
+
|
|
39
|
+
const url = `${context.baseUrl}/v1/threads/${request.threadId}/messages/${request.messageId}`;
|
|
40
|
+
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: 'GET',
|
|
43
|
+
headers: buildHeaders(context as RequestContext, {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
}),
|
|
46
|
+
...getRequestCredentials(context as RequestContext),
|
|
47
|
+
signal: controller.signal,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
clearTimeout(timeoutId);
|
|
51
|
+
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
console.warn(`TenxGraphClient: Thread message fetch failed with HTTP ${response.status}`);
|
|
54
|
+
const error = await createErrorFromResponse(
|
|
55
|
+
response,
|
|
56
|
+
'Thread message fetch failed',
|
|
57
|
+
'/v1/threads/{thread_id}/messages/{message_id}',
|
|
58
|
+
'GET'
|
|
59
|
+
);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const data: ThreadMessageResponse = await response.json();
|
|
64
|
+
|
|
65
|
+
if (context.debug) {
|
|
66
|
+
console.info('TenxGraphClient: Thread message fetched successfully', data);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return data;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (context.debug) {
|
|
72
|
+
console.debug('TenxGraphClient: Thread message fetch failed:', error);
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|